WordPress.org

Make WordPress Core

Opened 6 years ago

Closed 4 years ago

Last modified 4 years ago

#26877 closed enhancement (duplicate)

Allow get_post() to accept a custom post object instead of only WP_Post

Reported by: MikeSchinkel Owned by:
Milestone: Priority: normal
Severity: normal Version:
Component: Posts, Post Types Keywords:
Focuses: Cc:
PR Number:

Description

I'm refactoring a poorly coded theme that I want to use for it's design and I am finding it very helpful to be able to create a theme-specific Post class that encapsulates theme-specific behaviors related to Posts that the theme currently has "littered" over many different templates. (I usually build plugins and (almost?) never build themes but it seems this would be useful when building themes too.)

So I created a class that looks like this for my refactoring efforts (the following is only a small segment of the class but I've only included the relevant portions for this ticket):

/**
 * Class MyTheme_Post
 */
class MyTheme_Post {

  private $_post;
  private $_post_stack = array();

  /**
   * @param int|WP_Post $post
   */
  function __construct( $post ) {
    if ( is_numeric( $post ) ) {
      $post = get_post( $post );
    }
    $this->_post = $post;
  }

  /**
   * Get the title by using the_title() function.
   * 
   * @param string $before
   * @param string $after
   *
   * @return string
   */
  function title( $before = '', $after = '' ) {
    $this->_push_post();
    $title = the_title( $before, $after, false );
    $this->_pop_post();
    return $title;
  }

  /**
   * Set global $post to contain the inner WP_Post and push the prior onto an internal stack.
   * We need this so that get_post() will work correctly.
   */
  function _push_post() {
    global $post;
    array_push( $this->_post_stack, $post );
    $post = $this->_post;
  }

  /**
   * Pop the prior post off the internal stack.
   * We need this so that get_post() will work correctly.
   */
  function _pop_post() {
    global $post;
    $post = array_pop( $this->_post_stack );
  }

  /**              
   * Delegate any property requests to the contained post.
   * 
   * @param string $property
   *
   * @return array|mixed
   */
  function __get( $property ) {
    return $this->_post->$property;
  }

}

And then at the top of my theme template I do the following:

$post = new MyTheme_Post( $post );

This works brilliantly except unfortunately whenever get_post() is called by any template tag or other function, and lots of functions call it; get_post() throws away the custom instance and creates a new instance of WP_Post for use instead. You see how I get around this in $post->title() above, but I can't always do that.

I can envision two (2) fixes for this:

  1. Remove final from the WP_Post class so I can really subclass it, or
  2. Have get_post() inspect a special property (maybe $post->is_custom) and if it's set to true then assume it's an object the developer wants to keep.

I know that subclassing was not wanted for WP_Post when it was being implemented so if that's still the case I'm hoping we could at least do option 2.

Patch attached.

Attachments (3)

get_post_for_custom_post_classes.diff (946 bytes) - added by MikeSchinkel 6 years ago.
Patch to allow get_post() to check for is_custom property
26877.patch (1.2 KB) - added by MikeSchinkel 4 years ago.
Patch adds a 'get_post' filter to get_post()
26877.2.patch (1.2 KB) - added by MikeSchinkel 4 years ago.
Adds filter for get_post() named 'get_custom_post_object'

Download all attachments as: .zip

Change History (11)

@MikeSchinkel
6 years ago

Patch to allow get_post() to check for is_custom property

#1 @danielbachhuber
6 years ago

  • Milestone changed from Awaiting Review to Future Release

Just ran into a very similar use-case. #24672 looks related to me. I suspect we need to:

  • Finish classes for the other WordPress objects.
  • Remove the final from WP_Post et all so they can be properly subclassed.
  • Allow WP_Query and similar to return subclass instances.

#2 follow-up: @wonderboymusic
6 years ago

This isn't very convincing, and wasn't on the other ticket you pasted the same code example. You want to subclass a model so that you can use a theme function on one of the global $post's properties? Is this really worth 2 separate tickets?

#3 in reply to: ↑ 2 @MikeSchinkel
6 years ago

Replying to wonderboymusic:

This isn't very convincing, and wasn't on the other ticket you pasted the same code example.
Is this really worth 2 separate tickets?

I assume you are replying to me, the OP? If yes, what other ticket are you talking about? In my memory I only posted this one ticket on this topic.

You want to subclass a model so that you can use a theme function on one of the global $post's properties?

I don't understand what you mean by "use a theme function"; Are you saying that because I chose title() as the one methods I showed (vs. one of the other 25+ methods in my actual class) that I am "using a theme function?" If yes, note that rather than overwhelm the reader I was trying to show the smallest example I could, and yes $post->title() is not that beneficial. But $post->the_video_embed(), $post->the_featured_image() and $post->do_show_excerpt() might help you visualize it better.

What I want to do is be able to add properties and behavior to the $post object so that when I pass the $post object around, especially through other people's code that the properties and behavior follow along with it. This is just OOP 101 and wasn't something I though needed to be made highly convincing.

I understand and appreciate @nacin's reluctance to remove final which is why my patch did not propose final but instead used containment. The problem is we can't use containment if all the methods that would work with posts throw away our instances when they call get_post() which is the reason for my proposal.

If someone wants to add their own functionality like this they can't do it compatibly with WordPress; they have to write a fully custom theme vs. just add their own selected theme template files.

Last edited 4 years ago by MikeSchinkel (previous) (diff)

#4 @dbisso
5 years ago

This would have been really useful to me on a recent client project that needed a number of custom post types with additional methods and properties (e.g an event post object with $event_post->get_start_date() / $event_post->get_next_occurrence() methods etc). I resorted to a similar workaround as the OP, but had the same problem of getting get_post to accept the new object.

Ideally, I would love to see this included in 4.1 with #12955 which would allow a single, universal place to filter or replace post objects with custom classes.

@MikeSchinkel
4 years ago

Patch adds a 'get_post' filter to get_post()

@MikeSchinkel
4 years ago

Adds filter for get_post() named 'get_custom_post_object'

#5 @MikeSchinkel
4 years ago

  • Keywords 2nd-opinion added

Added a patch with a different approach that has a 'get_custom_post_object' filter in get_post().

#6 @MikeSchinkel
4 years ago

Related #30292

Last edited 4 years ago by MikeSchinkel (previous) (diff)

#7 @MikeSchinkel
4 years ago

  • Keywords has-patch dev-feedback 2nd-opinion removed
  • Resolution set to duplicate
  • Status changed from new to closed

Closing this ticket and moving attention to #30292.

#8 @netweb
4 years ago

  • Milestone Future Release deleted
Note: See TracTickets for help on using tickets.