7

When creating meta boxes, in each meta box function it seems a reference to the global $post is passed as a parameter ($event). I prefer this as it seems consistent and less likely to fudge the $post var by declaring it explicitly as I have read elsewhere.

add_action('admin_init', 'events_admin');
function events_admin() 
{
     add_meta_box('display_events_date_meta_box',
        'Dates',
        'display_events_date_meta_box',
        'events', 'normal', 'high'
    );

}

function display_events_date_meta_box($event) // Referenced
{
     //$event in this case is the $post global
}

I have created a filter and various other functions which currently just use the global $post variable.

add_action( 'admin_head-post-new.php', 'test' );
add_action( 'admin_head-post.php', 'test' );
function test()
{
    global $post; // Declared explicitly
}

Is there a standard / recommended way to pass the global $post variable as a parameter to these functions?

8

When I need to deal with $post variable on admin, I usually use a class to early catch and wrap global $post variable, obtaining an universal way to access to it, without repetitely relying on the global variable.

class MyAdminPost 
{

   private static $post;

   public static function init()
   {
     $p_get = filter_input(INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT);
     $p_post = filter_input(INPUT_POST, 'post', FILTER_SANITIZE_NUMBER_INT);
     if ($p_get > 0 || $p_post > 0) {
       self::$post = $p_get > 0 ? get_post($p_get) : get_post($p_post);
     } elseif ($GLOBALS['pagenow'] === 'post-new.php') {
       add_action('new_to_auto-draft', function(\WP_Post $post) {
         if (is_null(MyAdminPost::$post)) {
           MyAdminPost::$post = $post;
         }
       }, 0);
     }
   }

   public function get()
   {
     return self::$post;
   }
}

add_action('admin_init', array('MyAdminPost', 'init'));

On early stage of admin loading, that is 'admin_init' hook, 'MyAdminPost' class looks for post ID variable sent with request and store related post object.

That works on post.php page, but not on post-new.php, because on that page post ID is not sent with request because it does't exist yet. In that case I add a callback to 'new_to_auto-draft' that is one the "{old_status}_to_{new_status}" hooks to store the post immediately after it is created on post-new.php page.

In this way, in both pages, post object is stored in a class property very early.

Usage Example (Procedural)

function get_my_admin_post()
{
   static $post = null;
   if (is_null($post) && did_action('admin_init')) {
     $map = new MyAdminPost();
     $post = $map->get(); 
   }

   return $post;
}

add_action('admin_head-post.php', 'test');

function test()
{
    $post = get_my_admin_post();
}

Usage Example (OOP)

class ClassThatUsesPostObject
{

  private $post_provider;

  function __construct(MyAdminPost $map)
  {
     $this->post_provider = $map;
  }

  function doSomethingWithPost()
  {
    $post = $this->post_provider->get();
  }
}

Benefits

  • you can obtain post object very early in a way that is compatible to both post.php and post-new.php pages, so in all the hooks fired in those pages you have access to post object with no effort

  • you remove any global $post variable reference in your code

  • your code becomes testable in isolation

  • The Procedural and OOP code were very helpful, thankyou. +1 – myol Jan 10 '15 at 20:39
  • 1
    @MarkKaplun Not agree. This class responsibility is: "Give the post object of the related admin post page I am in". In admin post page, there's only one post that's under editing: you can manipulate a lot of posts, but while in admin post page if you want to know which post you're editing this class is perfect. Moreover, if some code change global post in admin post page w/out redirecting page or w/out restoring it after a temporary switching (for any reason) that code is a bug. Even during temporary swithing, class is useful if you want to know the post whose editing page you're in. – gmazzap Jan 11 '15 at 11:46
  • 1
    @MarkKaplun As a comparison, think in frontend to a page template that is used to display a secondary loop. During that loop, global post refers to every post in the loop. Fine. But if before, during or after that secondary loop you need the page object of the page you are in? There is get_queried_object() that's handy. It is available in admin too, but doesn't work for new post page and is available only after the query. This class fills the gap. – gmazzap Jan 11 '15 at 12:03
  • 1
    @MarkKaplun If I catch you or anyone else modifying globals on the fly, I'll personally shoot the one responsible exactly where he stands. – kaiser Jan 14 '15 at 19:47
  • 1
    Because is how $_REQUEST works. Try to send 2 values for same variable, but one on $_GET and one on $_POST. If you get the value with $_REQUEST the value on $_GET wins. @kaiser – gmazzap Jan 14 '15 at 21:37
1

You can not easily modify the parameters used with an existing filter/action as they are set by the "calling" code, therefor you can't inject additional parameters. There might be some possibility to do that by hacking the core code behind do_action/apply_filter but if you will succeed you most likely will break all the other code that hooks on those actions/filters.

1

admin_head-{$hook_suffix} is triggered on every admin page, so passing anything to that function probably wouldn't make sense. You can see in source that no arguments are passed:

do_action( "admin_head-$hook_suffix" );

In many cases you have no alternative to global $post, and if you look through just about any core source file, you'll see global litter everywhere, due to the mostly procedural construction of core and a primary objective to maintain backward compatibility. It's messy but mostly harmless if you're just accessing its value within a known context.

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.