Intercepting the WordPress Loop with Pre_get_posts

#deprecated (see http://codex.wordpress.org/Function_Reference/query_posts#Post_.26_Page_Parameters)

Using the “pre_get_posts” filter you can intercept the WordPress loop and override or set attributes in the “$query” object. This is useful because you can select just the posts you want, say for instance on your homepage you want to show posts from just one category.

add_filter('pre_get_posts', 'filter_homepage_posts');
function filter_homepage_posts($query) {
    $limit_number_of_posts = 5;
    $featured_category_id = get_cat_id('Reviews'); // by cat name...
    if ($query->is_home) {
        $query->set('cat', $featured_category_id);
        $query->set('showposts', $limit_number_of_posts);
    }
  return $query;
}
  • Julian

    Thanks – this is doing just what I was looking for – almost! The thing is, as well as filtering the posts correctly it stopped my custom menus from displaying in the sidebar – any idea why that would be or how to fix it?

    • bseanvt

      was you custom menu using a category?

    • http://cxzcxz.com nocash

      I experienced this same issue. It seems that menu items are actually stored in the wp_posts table with a post type of nav_menu_item. When WordPress attempts to pull menu items from the database, it does so using the same type of query that triggers the pre_get_posts filter, which is now filtered to a specific category and prevents the query from finding any menu items.

      A quick and dirty workaround is to add extra conditions to the function that is doing the pre_get_posts filtering to make sure it’s really working with posts or pages.


      function filter_homepage_posts($query) {
      if ( $query->post_type == 'page' || $query->post_type == 'post' ) {
      $limit_number_of_posts = 5;
      $featured_category_id = get_cat_id('Reviews'); // by cat name...
      if ($query->is_home) {
      $query->set('cat', $featured_category_id);
      $query->set('showposts', $limit_number_of_posts);
      }
      }
      return $query;
      }

  • http://wordpress.org/ wp-coder-101

    Please switch to using posts_per_page, show_posts will be deprecated..

    Your show_posts values end up being passed onto posts_per_page anyway…(because it replaces the older showposts).

    http://codex.wordpress.org/Function_Reference/query_posts#Post_.26_Page_Parameters

  • http://yochicago.com Rich

    Just a quick thank you for this post.
    I was having the same issue with my menu disappearing because I was filtering my query on post_type and didn’t realize the menu called the pre_get_posts filter. I’ve added logic to check for the menu query so it doesn’t filter accordingly.

  • http://iandunn.name Ian Dunn

    Just a minor correction: pre_get_posts is an action, not a filter. You should be hooking in with add_action() instead of add_filter(), and the callback function shouldn’t return anything.

    The $query variable is passed by reference, so it isn’t a copy of the $query variable inside WP_Query::get_posts(), it’s a link directly to it. Any changes to $query inside the callback function will be applied to the variable in the calling function.

    • bseanvt

      thank you for the clarification!