Добавление дополнительных данных к объекту WP_Post в WordPress
Я хочу добавить дополнительные элементы к данным, возвращаемым как WP_Post
. Чтобы для каждой функции/запроса, возвращающего объект WP_Post, мои дополнительные данные добавлялись автоматически.
Пример возвращаемого результата:
WP_Post (object) => [
// Стандартные данные, возвращаемые WP_Post
ID => int,
post_author => string,
post_name => string,
post_type => string,
post_title => string,
post_date => string,
post_date_gmt => string,
post_content => string,
post_excerpt => string,
post_status => string,
comment_status => string,
ping_status => string,
post_password => string,
post_parent => int,
post_modified => string,
post_modified_gmt => string,
comment_count => string,
menu_order => string,
// Дополнительные данные, которые я хочу добавить
extra_data_1 => array,
more_data_key => string,
another_added => string
]
Например, когда функции get_post()
или get_page_by_path()
выполняются, они должны возвращать объект WP_Post вместе с моими дополнительными данными.
Я пытался найти подходящий хук/фильтр, но это не увенчалось успехом.
Я надеюсь, что смогу сделать что-то вроде:
// Это концептуальный код
add_action('pre_wp_post_return', function($data) {
$data->extra_data_1 = get_post_meta($data->ID, 'extra_data');
$data->more_data_key = get_post_meta($data->ID, 'more_data', true);
$data->another_added = get_post_meta($data->ID, 'another_data', true);
return $data;
});
Моя логика в том, что я создаю кастомный API для WordPress, который использует различные основные функции, возвращающие объекты WP_Post. Возможность добавить дополнительные данные в одном месте позволит избежать дублирования кода.
Надеюсь, мое объяснение достаточно понятно. Любая помощь будет очень полезна!
Если ваши дополнительные данные напрямую ссылаются на метаданные записи, вам не нужно ничего делать, потому что WP_Post
реализует "волшебные" методы __isset()
и __get()
, которые напрямую запрашивают ключи метаданных записи (за исключением следующих четырёх ключей: page_template
, post_category
, tags_input
и ancestors
). Вот краткий пример, демонстрирующий это поведение:
<?php
$post_id = 42;
$meta_key = 'extra_data_1';
add_post_meta( $post_id, $meta_key, [ 'some', 'value' ], TRUE );
$post = get_post( $post_id );
var_dump( $post->{$meta_key} ); // (array) ['some', 'value']
В любом другом случае используйте фильтр posts_results
:
<?php
add_filter(
'posts_results',
function( array $posts, WP_Query $query ) {
foreach ( $posts as $post ) {
$post->extra_data_1 = get_post_meta( $post->ID, 'extra_data' );
// и так далее …
}
return $posts;
},
10,
2
);
Однако я бы предложил использовать объектно-ориентированный подход и создать собственные интерфейсы сущностей, которые соответствуют вашей предметной области. Реализации затем будут оборачивать экземпляры WP_Post
в качестве зависимости. Например, предположим, что вы работаете с книгами:
<?php
namespace Wpse240042\Type;
use WP_Post;
interface Book {
/**
* @return string
*/
public function title();
/**
* @return string
*/
public function publisher();
/**
* @return int
*/
public function year();
}
class WpPostBook implements Book {
/**
* @var WP_Post
*/
private $post;
/**
* @param WP_Post $post
*/
public function __construct( WP_Post $post ) {
$this->post = $post;
}
/**
* @return string
*/
public function title() {
return $this->post->post_title;
}
/**
* @return string
*/
public function publisher() {
return get_post_meta( $this->post->ID, '_book_publisher', TRUE );
}
/**
* @return int
*/
public function year() {
return get_post_meta( $this->post->ID, '_book_publisher', TRUE );
}
}
Ваша бизнес-логика может затем полагаться на структуру книги, указывая тип Book
в каждой зависимости. Для получения списка книг вы можете реализовать фабрику на первом этапе, которая обернет WP_Query
или получит аргументы WP_Query и вернет список экземпляров книг. В этом случае не следует использовать фильтр posts_results
для замены WP_Query::posts
списком экземпляров Type\Book
, чтобы не нарушить согласованность типов в ядре WordPress.

TLDR; Вы не можете и не должны этого делать.
Это невозможно расширить класс WP_post
дополнительными полями, потому что он объявлен как 'final'. Возможно, вы могли бы обойти это, обернув класс в другой класс (руководство), но это всё равно не рекомендуется.
Все виды тем и плагинов полагаются на то, что метаданные хранятся как метаданные. Может быть, сейчас у вас получится заставить это работать, но, скорее всего, вы пожалеете об этом в будущем, когда обнаружите, что какой-то плагин, который вы хотите использовать, не может обработать способ хранения ваших метаданных.

Он просто хочет добавить публичные свойства к произвольному объекту. Это возможно независимо от расширяемости объявления класса. Также есть фильтр posts_result
.

@David Я проверил в комментариях к вопросу, OP явно хочет расширить класс wp_post
, а не использовать add_post_meta
.

Я знаю, что это старый пост, но решил поделиться рабочим решением. Это можно реализовать следующим образом:
$posts = get_posts(
array(
"post_type" => $post_type->name,
"numberposts" => -1
)
);
foreach($posts as &$post)
{
$custom_fields = (array)$post;
// добавляем дополнительные поля, например так
$custom_fields["posttype_link"] = get_permalink($post->ID);
$post = (object)$custom_fields;
}

Используйте хук the_post: https://developer.wordpress.org/reference/hooks/the_post/
Вы будете изменять глобальный объект $post, поэтому не нужно возвращать его, как в случае с фильтром. Два параметра $post и $query передаются по ссылке.
