4

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!

  • Is there a reason why you don't wat to use post_meta? – cjbj Sep 21 '16 at 10:13
  • @cjbj I have post meta applied to the posts but the WP_Post object does not include this by default. I have updated my question to stop confusion. 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. – Levi Cole Sep 21 '16 at 10:21
  • Understood your point. I'll delete my answer. I've just looked through post.php and class-wp-post and didn't find a single action hook or filter hook. That's bad news for the idea you're trying to create here. – Ahmed Fouad Sep 21 '16 at 10:23
  • To prevent duplicating code, why not write your own wrapper functions that return values from both the post object and its metadata? That seems a simpler approach. – Andy Macaulay-Brook Sep 21 '16 at 10:49
  • @AndyMacaulay-Brook This is currently how I am doing it but was wondering if there was a better solution. – Levi Cole Sep 21 '16 at 10:50
7

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.

1

TLDR; You can't and you shouldn't.

It is impossible to extend the WP_post class with extra fields, because it has been defined as 'final'. You perhaps could get around that by wrapping the class in another class (tutorial), but it still is not advisable.

All kinds of themes and plugins rely on the metadata being stored as metadata. You may get it to work right now, but you'll probably regret it in the future, when you find out that some plugin you want to use cannot handle the way you have stored your metadata.

  • He just want to add public properties to an arbitrary object. This is possible irrespective of the extensibility of the class declaration. Also there's the posts_result filter. – David Sep 21 '16 at 10:43
  • @David I checked that in the comments to the question, OP explicitly wants to extend the wp_post class, not use add_post_meta. – cjbj Sep 21 '16 at 10:46
  • 1
    For what I see he/she wants to extend post objects not the class declaration. – David Sep 21 '16 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.