Adding additional data to WP_Post object

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
4

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.

Leave a Comment