Obliterate the main query and replace it

When 'pre_get_posts' is fired, there are a lot of things that WordPress has already done like setup all the query variables (also the query you don’t send) and setup all the conditional properties (all the is_* properties). So, if you want completely replace the query you should reset all that things and set your own.

In fact, even if your code works, WordPress will use page.php as template.

However there’s a simpler approach: act before WordPress do all the things.

A good place is the 'request' filter hook, at that time WordPress has created the query vars from url and is going to pass that vars to WP_Query, and you can intercept and change query arguments before are sent.

That action is fired by WP class, and pass the query vars to be set (see code).

add_filter( 'request', function( array $query_vars ) {

  // triggered also in admin pages
  if ( is_admin() )
    return $query_vars;

  // you should check also for page slug, because when pretty permalink are active
  // WordPress use 'pagename' query vars, not 'page_id'
  $id = isset($query_vars['page_id']) && (int) $query_vars['page_id'] === 84;
  // remember to replace "slug" with real page slug
  $name = isset($query_vars['pagename']) && $query_vars['pagename'] === 'slug';

  if ( ( $id || $name)  && ! isset($query_vars['error']) ) {
    $query_vars = array('category_name' => 'special-announcement');
  }

  return $query_vars;

});

Replace 'slug' with the real slug of your page, then let WordPress do its work.

Note that I’ve not check for main query, because 'request' is triggered only for main query.

If you want to use a specific template, you should use a filter on template_include otherwise 'category.php' (or the right template according to template hierarchy) will be used.

Leave a Comment