When to use pre_get_posts in WordPress

Many of us are unaware about pre_get_posts hook that is very handy when it comes to alter the main loop. I got to know about this handy filter via WordPress review team while submitting a theme to WordPress.org. So I will try to explain as simply as I could about its uses, benefits and when to use it.
Actually pre_get_posts hook is executed after the query variable object is created but before the main query runs. While using pre_get_posts hook, $query hook is passed by reference this means any changes we make to the object inside our function are made directly to original object.

So why to use pre_get_posts hook when we have query_posts and wp_query to alter the loop. As I emphasized in my previous article too, I would suggest better not to use query_posts anymore. Because it has lots of drawbacks then its benefits. Here I would like to clarify with a simple example about the benefits of using pre_get_posts hook.

Suppose we want to alter the normal posts that are being displayed in home page. And instead of showing all the posts we want to display posts except from certain category. So generally what we do is use query_posts or wp_query to achieve this. This will do the work but we are not thinking the extra burden that we are giving to WordPress. So here’s what actually happen before the Posts are being rendered in the browsers. First WordPress creates its query object runs its default query and then run inside the template. But inside the template the WordPress sees query_posts ordering to show posts from only certain category. WordPress then dumps its original query value aside and runs to fetch as been specified in query_posts. So Why not use pre_get_posts hook and tell WordPress before the default query is executed instead of telling it later. By doing this we are giving less burden to WordPress as it have to execute less query.
So here how this can be achieved.

/**
 * Display posts at home page except from category with id 1 and 2
 * Alter the main loop
 * @uses pre_get_posts hook
*/
function exclude_category( $query ) {
    if ( $query->is_home() && $query->is_main_query() ) {
        $query->set( 'cat', '-1,-2' );
    }
}
add_action( 'pre_get_posts', 'exclude_category' );

/**
 * Change posts per page
 */
function change_post_per_page( $query ) {
    if ( $query->is_main_query() && $query->is_home() ) {
        //Display only 1 post for the original blog archive
        $query->query_vars['posts_per_page'] = 1;
        return;
    }
     if ( $query->is_main_query() && $query->is_post_type_archive('movie') ) {
        //Display 20 posts for a custom post type called 'movie'
        $query->query_vars['posts_per_page'] = 20;
        return;
    }
}
add_action('pre_get_posts', 'change_post_per_page', 1);

But we have to be very careful while using pre_get_posts hook because this hook is fired for every post query

 

  • get_posts()
  • new wp_query()
  • random recent posts widget
  • Everything

 

So to avoid this we use conditional tag is_main_query() then filter will fire only on main loop.This filter affects admin screen queries also. So it will be better to make sure is_admin() return false.
Then comes the question when to use wp_query. Well wp_query are to be used for secondary loops for e.g. in sidebars to show popular posts or when using multiple loops within single page.
Moreover using pre_get_posts hooks has another advantage that it uses global query object so there is no issues regarding pagination which is a major headache while using query_posts.

When to use pre_get_posts in WordPress
Tagged on: