I want to add additional items to the data returned as WP_Post
. So for every function/query that returns a WP_Post object I want my additional data added.
Example of returned result:
WP_Post (object) => [
// Default data returned by 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,
// Additional data I want to add
extra_data_1 => array,
more_data_key => string,
another_added => string
]
For example when the functions get_post()
or get_page_by_path()
are run they will return the WP_Post object along with my additional data.
I’ve tried finding the appropriate hook/filter but this has been unsuccessful.
I am hoping I can do something like:
// This is concept code
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;
});
My reasoning is I am having to build a custom API for WP that uses a range of different core functions which return WP_Post objects. Being able to add my additional data in one place would prevent me from duplicating code.
I hope this is clear enough. Any help would be great!
4 s
If your extra data directly references a post meta you don’t have to do anything, because WP_Post
implements the »magic« methods __isset()
and __get()
which directly asks for post meta keys (except for the following four keys: page_template
, post_category
, tags_input
and ancestors
). Here’s a quick example that shows the behavior:
<?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']
In any other case, use the filter 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' );
// and so on …
}
return $posts;
},
10,
2
);
However I would suggest to use an object oriented approach and create own entity interfaces, that follows your problem domain. The implementations then wrapping WP_Post
instances as a dependency. For example, let’s say you’re dealing with books:
<?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 );
}
}
Your business logic can then rely on the structure of a book by type hint the type Book
on every dependency. To fetch a list of books you could implement a factory in the first step that wraps a WP_Query
or fetch WP_Query arguments and return a list of book instances. You should not use the posts_results
filter in that case to replace WP_Query::posts
with a list of Type\Book
instances to not break type consistency throughout WP core.