Blog

Sharing WordPress tips and tricks as I find them

Customizing the WordPress Query

One of the most powerful features of WordPress is the WP Query. It is what determines what content is displayed on what page. And often you’ll want to modify this query to your specific needs. Some examples:

  • Don’t display posts from Category X on the homepage
  • Increase or decrease the number of posts displayed per page for a specific post type
  • Determine which posts are shown and their order based on postmeta
  • Inside your page, do a separate query for different content

Andrew Nacin recently gave a great talk on using the query, which you can look at for some more technical information on what’s happening behind the scenes. I’m going to focus this tutorial on common uses.

First, what not to do. Don’t use query_posts(). As you can see from the Codex page, there’s a lot of caveats to it. I really can’t think of an instance where this function is advisable.

There’s two approaches you should take depending on your needs.

Create a new query to run inside your page or template.

This is best when the content you’re displaying is being loaded in addition to your current page’s content. For instance, if you had a page about Spain and wanted to show your 5 most recent blog posts about Spain at the bottom.

For this you’ll use the WP_Query class. For more information, see my post on Custom WordPress Queries. Also take a look at my Display Posts Shortcode plugin, which might save you from having to write any code.

Customize the Main Query

If you want to alter which content is returned on a page, you most likely will be modifying the main query. The first three examples above all require altering the main query.

WordPress has a very handy hook called pre_get_posts. It fires once all the query settings are ready but right before the actual query takes place. This is where we’ll jump in and modify the query settings if needed.

You must place your function in functions.php (or a plugin). WordPress needs to build the query to figure out what template to load, so if you put this in a template file like archive.php it will be too late.

All our functions are going to have a similar structure. First we’re going to make sure we’re accessing the main query. If we don’t check this first our code will affect everything from nav menus to recent comments widgets. We’ll do this by checking $query->is_main_query(). This was added in WordPress 3.3. For earlier versions, compare $query to the global $wp_the_query variable, as shown below in the 3.2 code below.

Then we’ll check to make sure the conditions are right for our modification. If you only want it on the homepage, we’ll make sure the query is for home ( $query->is_home() ).

Finally, we’ll make our modification by using the $query->set( 'key', 'value' ) method. To see all possible modifications you can make to the query, review the WP_Query Codex page.

Exclude Category from Blog

We’re checking to make sure the $query is the main query, and we’re making sure we’re on the blog homepage using is_home(). When those are true, we set ‘cat’ equal to ‘-4′, which tells WordPress to exclude this category.

If we were using WordPress 3.2 or earlier, the code could look like this:

Change Posts Per Page

Let’s say you have a custom post type called Event. You’re displaying events in three columns, so instead of the default 10 posts per page you want 18. If you go to Settings > Reading and change the posts per page, it will affect your blog posts as well as your events.

We’ll use pre_get_posts to modify the posts_per_page only when the following conditions are met:

  • On the main query
  • Not in the admin area (we only want this affecting the frontend display)
  • On the events post type archive page

Modify Query based on Post Meta

This example is a little more complex. We want to make some more changes to our Event post type. In addition to changing the posts_per_page, we want to only show upcoming or active events (so not ones that have ended), and sort them by start date with the soonest first.

I’m storing Start Date and End Date in postmeta as UNIX timestamps. With UNIX timestamps, tomorrow will always be a larger number than today, so in our query we can simply make sure the end date is greater than right now.

Creating the metabox and the fields within it are beyond the scope of this tutorial, but take a look at my post on Custom Metaboxes and this Events Plugin if you you want more informaton.

If all the conditions are met, here’s the modifications we’ll do to the query:

  • Do a meta query to ensure the end date is greater than today
  • Order by meta_value_num (the value of a meta field)
  • Set the ‘meta_key’ to the start date, so that’s the meta field that posts are sorted by
  • Put it in ascending order, so events starting sooner are before the later ones

118 Comments

  1. Awesome post, Bill, and most serendipitous in the timing. I’d just started going through the Codex on the WordPress query and this gave me a great leg up. Thanks very much.

  2. Useful post, Bill – thanks! I really like your Gist implementation within this blog. I just started using GitHub myself recently and like it more very single day. It’s irrelevant question, but if you don’t mind, what are the benefits of using Gist for code snippet examples that you’re using within the blog? Are you re-using snippets later or collaborating via Gist on them? I’m just curious about usage cases. Thanks!

    • Bill Erickson says:

      I do plan to reuse them. I’m on the StudioPress support forums a lot and when someone is asking how to do a specific change I’ll post a link directly to the code rather than the whole post.

      But mainly I’ve found it’s easier to maintain and control the code if it’s embedded using Gist than directly added to the post. When I change up my theme I have to go through and make sure all my past code snippets didn’t break (and they have quite a few times).

  3. This post would be the perfect place to show off how easy it is to run a simple custom query with Genesis!

    • Bill Erickson says:

      Actually, the ‘query_args’ custom field that StudioPress recommends you use also falls under my “Don’t do this” recommendation. When you use it, Genesis is adding query_posts() to it. So if you use the blog page template and put ‘posts_per_page=5′ in the ‘query_args’ custom field, your pagination will be messed up for the reason I describe above.

      Even on Genesis I recommend you use the approach detailed above. If you’re modifying the main query (which is what you’re doing if you’re using the ‘query_args’ custom field), you should do it using the pre_get_posts hook.

  4. Thanks Bill! I’ve recently written a post on my blog (harriswebsolutions.co.uk/blog/2011/create-your-own-archive-page-with-wordpress/) on a very similar theme. But I’ve altered the query directly with SQL statements rather than using the $wp_the_query; object. Your method is much cleaner I feel. How would (is it even possible?) to use it to access other tables though (say, from a plug-in)?

  5. thanks for the tutorial. I used it to filter posts by a custom field value with BETWEEN to allow searching within a numeric range.

  6. Bill – just reaching out here in case you have time to share some advice. I’m not that familiar with the WP Query big picture.

    I have a need to have sticky posts for categories and to _not_ have sticky posts for the home page. Basically opposite of what the query.php does.

    In WP 3.2.1, I was just able to change it from “if( $this->is_home && …” to “if( !(this->is_home) && ….” and it worked like I expected.
    When I upgraded to WP 3.3, that simple change no longer works. It causes single post pages to fail (actually just shows the same post no matter what pageid i am trying).
    I’m guessing the three other conditions on this line (line 2692 in query.php) don’t help indicate if it’s a category list vs a single post, and it was dumb luck to work in WP 3.2.1.

    I’d love to use a cleaner method to do this with add_action or add_filter, or whatever else, but I don’t understand the constructs very well. I also wonder if they will even work, because the query is coded to do the is_home check, so i’m not sure how a filter/action would change that.
    I’m hoping to not have to rewrite the entire query.php in my template, but that seems to be what I’ll need to do.

    Any advice is greatly appreciated.

    • Bill Erickson says:

      To be honest, I don’t have much experience with sticky posts and wouldn’t use them. They’re a relic from an old version of WordPress, and the functionality you’re looking for can be better implemented with post meta.

      1. Add a metabox to the edit post screen that has a “Category Featured” checkbox.
      2. On category.php, add a custom loop at the top that lists the category featured posts. Your query might be something like this: https://gist.github.com/1519651
      3. In functions.php, exclude featured posts from the main query on category pages. Something like this: https://gist.github.com/1519659

      Note that none of the code above is tested, I just typed it real quick in GitHub. It might not work, but should point you in the right direction.

      • Thanks for the pointers!! I’m working on implementing this now. This is something I want to be theme independent, so I’m trying to combine your custom metabox idea (which is made to add to a theme) with your custom function (core functionality) plug-in idea.
        Being new to this, I’m not 100% sure how to combine them. Any tips there?

        • Bill Erickson says:

          If you take a look at my core functionality plugin, you’ll see that it already has metaboxes built-in. You just have to uncomment one line in plugin.php, then create your metaboxes in /lib/functions/metaboxes.php.

          • I noticed that as I started digging in – good stuff, thank you! I spent 2 hours trying to debug why it wouldn’t work only to discover I had to change ‘pages’ => array(‘pages’) to ‘pages’ => array(‘post’).

            I haven’t got the page/category loop implemented yet, but thanks to your code mock up, I’m on my way. Thanks for your help!

  7. Just wanted to follow up and say thanks again. I was able to get the double loop working in my theme’s archive.php.

    For the first loop, I used array_merge to keep the current wp_query args, and then added the argument to check for the category featured metabox setting i added using your custom function code.
    For the second loop, i actually had to loop through the results of the first query and create an array of post id’s and then used that to pass as a post__not_in argument to a new wp_query.

    Works great- thanks for the help!

  8. Is it possible to order posts by the child term on the parent term index template with this method?

    • Bill Erickson says:

      I don’t believe so. If you look at the WP_Query page in the codex, you’ll see all the ways you can order the results.

      I think you’ll have to do a custom loop for that. In your taxonomy template, check to see if the current tax term has any children. If it does, loop through each child and pull those posts: https://gist.github.com/1597218

      Note that this is very inefficient (there’s a lot of extra queries going on in there). Another approach could be to do a preliminary loop through the posts, storing each post object in an array and organized by tax term. Then sort that array by the tax term, and use the results to actually display the posts.

      • thanks Bill, it seems your solution is the same as the one I found here:
        http://wpquestions.com/question/show/id/926

        with the advantage that you’re using WP_Query instead of query_posts, so I will modify it to use WP_Query.

        And I agree that getting the dataset first would be better to avoid multiple queries. I did something similar recently for an archives widget that grouped by year then month. Maybe I can merge all these ideas together :)

  9. Thanks – helped me out with the pagination problem where pg 2 on custom taxonomies shows a 404 unless you set the backend # of posts to less than the # you’re showing using the custom query on the page.

  10. thanks

  11. fallenboy says:

    Thanks you for the tips!

    I have a question tho — is it possible to make “Customize Event Query using Post Meta”-snippet to play well with calendar? I have tried to change the code but it doesnt seem to work :(

    The calendar makes URL for future ie. something.com/archive/2012/05. All i get is 404, even tho there are events in april :(

    Is it possible?

    Thank you!

    • Bill Erickson says:

      Based on your URL it looks like your calendar is using the post date, not a separate field in post meta. WordPress will only show posts that have been published already (so have a post date in the past).

      Create a custom metabox and create a field for event date (use the text_date_timestamp field type so it is a UNIX timestamp). Then you can use the event query code.

      • fallenboy says:

        Thanks for reply.

        The calendar generates link based on custom date — if in april there are events, it gives april’s link. Now when you go to that aadress, it checks posts — there are none, and it gives 404.

        if ( $query->is_main_query() && !is_admin() && is_post_type_archive( ‘event’ ) ) { <– this is never true because is_post_type_archive( 'event' ) is never true. :S

  12. chrismccoy says:

    how would you set the posts per page for an else?

    i set posts per page for is_search, on the search.php template the else shows 10 posts with random, was hoping to avoid doing query_posts on the search.php if it can be done via pre_get_posts

  13. chrismccoy says:
  14. I am build­ing a direc­tory via Word­Press and Stu­dio­Press and would like to know if I can use the query_arg method via cus­tom fields to pull posts/pages that are category1 and category2? After reading your post I’m starting to think this might not be the best method for what I need to do.

    I have cre­ated a cus­tom post type of Direc­tory and then have set-up mul­ti­ple tax­onomies with par­ent and child such as:

    Loca­tions — Par­ent
    City1 — Child
    City2 — Child
    City3 — Child

    Restau­rants — Par­ent
    Amer­i­can — Child
    Mex­i­can — Child
    Chi­nese — Child
    etc, etc, etc…

    I need to be able to have a page that will pull in all restau­rants that are in City1. Can I do it using the query_arg method and if not or not recommended, what is the best method to do this? I have to do this for a lot of pages and I’m try­ing to find a sim­ple solu­tion with­out hav­ing to build a page tem­plate for every sin­gle category. Can you help set me on the right path here so I do it right the first time?

    Any help you could pro­vide would be awe­some! Thanks!

    • Bill Erickson says:

      When you say “multiple taxonomies”, do you really mean you’ve created multiple taxonomies, or that you have a single taxonomy with multiple terms?

      If you create a taxonomy for Location and a taxonomy for Restaurants, then you can automatically view the posts matching a specific term using the taxonomy archive.

      If taxonomy = ‘location’ and term = ‘city-1′, the URL for the archive is yoursite.com/location/city-1. There’s no need to create a page – WordPress does this for you.

      As an example, see the code snippets section of my site. The post type archive is here ( http://www.billerickson.net/code/ ). I have a taxonomy called ‘code tags’, and here’s a term inside that taxonomy: http://www.billerickson.net/code-tag/metabox/

      • Hey Bill,

        Thanks for the reply. I think we may be onto something here, but I need to figure out how to pull a page that has all the restaurants in City1 as an example. If I were to give you the login to my site, would you be willing to look at it real quick to see my set-up and let me know if I am heading in the right direction with Custom Post Types and Taxonomies? If so, let me know how to get it to you and I will send it asap. Thanks for your help with this.

        • Bill Erickson says:

          I wish I could help but I don’t have any time to do free consultations (even though it should only take a few minutes). I have about 15 active projects right now and am hopping on a 5am flight to a WordCamp tomorrow morning.

          If you’d like, you can schedule to work with me in a few weeks. As of right now, I’m scheduling projects to start Monday, July 2nd.

          • Hey Bill,

            I respect your time and thank you for the help you have given thus far. If I can’t get this figured out here soon I might just take you up on your offer.

            Have a great time at WordCamp!

  15. Sorry, Bill. I’m new to commenting and did not realize I could not paste a php function in the input field here. I may be out of luck with this. :(

  16. Hi Bill!

    Thank you so much for getting back to me. I really appreciate it. I pasted the code like you said at the following URL:

    git clone git://gist.github.com/2883980.git gist-2883980

    The address of my site is: http://www.serv-hot.com but the site is in maintenance mode so you will need to login with the following credentials to take a look:

    USERNAME: admin
    PASSWORD: 3000hot

    (you will see the tiny ‘login’ button on the bottom right-hand side of the screen of the maintenance mode page.)

    Once inside you can navigate to the Appearance editor where you must choose the SIGHT theme from the pulldown menu above the list of template files. This is the theme that I’m using; not the default theme TWENTYELEVEN.

    Any insight you can provide on this issue will be a godsend. Thank you. Hope to hear from you soon. — monti

    • Bill Erickson says:

      Can you post just the specific code you have a question about, and the question itself?

      Logging into your site and troubleshooting is beyond the free help I provide on my site, but if you’d like to hire me feel free to use my contact form: http://www.billerickson.net/contact

  17. Bill, I used your first code under “Exclude Category from Blog” including it inside of home.php. It didn’t work. I tried several positions, in one of them it turns me the site completely blank.
    Can you give me your opinion to get me in the right direction. ?
    My site uses the Genesis Design Framework from StudioPress Version: 1.8.2 in a WordPress 3.4.1 platform.
    Thank you Bill & congratulations for you good job.
    Julio

    • Bill Erickson says:

      That’s actually a very common problem, which I addressed about halfway down the page in bold.

      You must place your function in functions.php (or a plugin). WordPress needs to build the query to figure out what template to load, so if you put this in a template file like home.php it will be too late.

      • Bill, I put the code in functions.php but it didn’t work .

        I think I have a BIG confusion. When this article starts you say “Some examples: Don’t display posts from Category X on the homepage.” I thought, that is what I want to do. On my WordPress Reading Settings I have My Front page displays>> Your latest post set. So my Homepage shows all my posts from the more recent in the top of the page. I’m not using the Blog Template in other words.

        MY CONFUSION is when you write about Exclude Category from Blog and you give the first code for copying and pasting it. Is that code exclusive only when you are using the Blog template ? Let’s say Settings>>Reading>>Front page displays>>A statis page>>Front page: Blog

        Is it a different code when y have my homepage not set as a Blog, only the default homepage?

        Sorry the mess, I can not explain it in other words.

        Thank you

        • Bill Erickson says:

          The is_home() parameter will return true only on the blog. This will be on the front page if you have your blog on the front page, or on some other page if you have the blog set to some other page.

          So this code should be working for you. Have you tried switching to the default WordPress theme and doing this code to see if it’s an issue with your theme? Your theme might be modifying the query incorrectly which is messing up your code.

          • I switched to the default WordPress theme Twenty Ten. Then I went to Theme Functions
            (functions.php), I went all the way to the bottom and pasted there your code on…(Exclude Category from Blog). And it showed all the posts excluding the ones corresponding to the category I inserted in your code ( ‘cat’, ‘-115′ ). I tested with others categories and it worked well.
            So which would it be the problem with the theme I want to use, Eleven40 Child theme ?
            Thank you Bill

  18. Julio, this is most likely because of the genesis grid loop. I’ve had issues with it since it creates a new query in the page rather than using the main one.

    You can open home.php and place your query arguments directly in the genesis_grid_loop() function, but you’ll get 404 errors when you get to the last of your blog posts (you’ll actually be getting 404 errors right now because of eleven40′s ‘posts_per_page’ => 5 argument).

    A better approach would be to rebuild the homepage to use the main WordPress query.

    • Hmmmm, I do not want to be up to my neck in mud.
      Ok, I want to determine how to show posts of a specified category in my homepage. It can be one specified category or two specified categories, etc, and filter or block the other categories, that’s my goal.
      As you publicize in your article…”Don’t display posts from Category X on the homepage”, that’s good, but it doesn’t work with my eleven40 child theme, I copied the code you obsequiously give us under “Exclude Category from Blog” and pasted it inside of function.php and it didn’t work.
      Bill when you write “You must place your function in functions.php (or a plugin)….” Which is that Plug-in ? Do you think that Plug-in could solve my problem instead of trying different codes in function.php?
      Is that Plug-in the one you present in “Also take a look at my Display Posts Shortcode plugin, ….”?
      Thanks Bill

      • Bill Erickson says:

        The proper method outlined above will not work with your theme unless you remove the genesis_grid_loop() function from your home.php file.

        Alternatively, you can use an improper method by modifying the genesis_grid_loop like this:
        Actual code: https://gist.github.com/3060649
        Difference: http://diffchecker.com/YVeR7tmA

        Change ‘homepage-posts’ to the category slug you want displayed on your homepage.

  19. Wes Linda says:

    So i’m attempting to figure out how to offset posts on the homepage of the new education theme from StudioPress. I’ve read the tutorial and I guess I’m simply unsure of how to do this. I’ve tried numerous ways shown on a number of sites, with no success. Any ideas?

  20. FINALLY!! Solved my problem. Added to functions.php child theme. I’m not a php programmer by any means. But i tried this and it worked. I don’t want the echo statement but couldn’t get correct syntax without it. So yeah… need a minor adjustment? but what?

    https://gist.github.com/02fae2548294fec336e2

  21. Gary Darling says:

    Great tutorial Bill. I’m trying to implement this on a site that uses `query_posts` in several template files, but I’m struggling with a custom query. The user wants his posts sorted by first by the meta_field ‘builder’, then by the meta_field ‘squareft’. So the result would be all builders named ‘A’ would be first, and within that group all his homes ordered by ’2000′, ’2100′, etc.

    I’ve tried variations of Orderby with two meta_values, with ‘title’ and ‘meta_value’, I tried two meta_keys, no luck. Any ideas? Here is the code I have now:

    http://gist.github.com/3678753

    • Bill Erickson says:

      I don’t believe you can have WordPress sort the results by two keys like that. You should build your query, then run through it once or twice to manually sort it the way you want, then output it.

  22. Thanks so much Bill for the tutorial.

    I looked at your example on excluding a category from the loop above, great! How do I exclude an array of post format from the loop? Let say I want to exclude aside and quote post format from the loop. Hope I didn’t ask you too much.

    • Bill Erickson says:

      Post formats is actually a taxonomy called ‘post_format’. So you would use a tax_query to interact with it.

      Here’s how you’d exclude aside and quote from the homepage: https://gist.github.com/3698518

      • Thanks so much for the code.

        However, I’ve problem with the code. My blog simply doesn’t show any blogpost with the message:

        “Sorry, no posts matched your criteria.”

        Here’s what I’ve in my functions.php https://gist.github.com/3712088 . Nothing much there, a fresh functions.php file from Sample Child Theme. I add support for post format and just paste your code above at the very bottom of the file.

        I’ve tried two ways:
        1. Since I’ve no home.php file, so I just leave it as it is. By default, genesis will show the loop of latest blogspot. No luck.
        2. Secondly, I tried creating a “blog” page, using the blog template. Then, from the reading settings page, I tick on the static page, and from the dropdown of “post page”, I chose the “blog” page I’ve created previously. No luck.

        Did I miss something here?

  23. Thank you for such great tutorial, very detailed explanation on how to use pre_get_posts.

    However, can pre_get_posts be used to be applied only to one custom query?

    Because, i want to list all posts with closed comments, only in one custom query that runs on one of my custom page templates.
    On another page i want to list all posts with opened comments, and on a third page i want to list all posts.

    Therefore, i’m using three different custom queries, the last one is simple to make, but i’m having problem with the first two – can i apply pre_get_posts on two different custom queries separately?

    • Bill Erickson says:

      pre_get_posts should only be used for the main page’s query (so on a custom page template, rendering the content entered into the Edit Page screen). For what you’re looking to do, you’ll want to make a custom query using WP_Query().

      • Thanks for the reply,
        but how can i include the comment status into a custom query?

        Under $args for WP_Query, there is nothing related to comment status?

        I have this bit of code https://gist.github.com/3709991
        Here, i display 10 posts per page, and for each post check if the comments are open – but that breaks my pagination – if among 10 posts, there are no posts with open comments, it will display a blank page, and i don’t want that.
        Or, if there are 2 posts with open comments among 10 posts, it will display 2 posts on a page.

        I want to list all posts with open comments, 10 posts per page.

        • Bill Erickson says:

          It doesn’t look like comment status is an available query arg. You’ll need to craft a custom SQL query that looks for posts with ‘open’ in the ‘comment_status’ column of the posts table.

        • Bill Erickson says:

          I don’t see anywhere on that page where it says comment_status can be used with pre_get_posts. The only place comment_status is located on that page is in showing what is returned in the $post.

  24. Bill,
    I used your “Better, and easier grid loop” tutorial to display my CPT in a grid format (worked great, by the way). Now I’m simply trying to add those CPT posts to the main query.

    After reading your tutorial on customizing the query, I’m still not clear on how to simply include a category. If cat,-4 excludes a given category, does cat,+4 include one?

    • Bill Erickson says:

      Are you trying to mix in your custom post type posts (ex: ‘products’ post type) into your blog ( the ‘post’ post type)? If so, use $query->set( ‘post_type’, array( ‘post’, ‘products’ ) )

      • Yes…that is what I’m trying to do. I’ll try the code you provided.

        One more question:
        On the corresponding add_action, could I use pre-get_posts?

        • Bill Erickson says:

          Yes, here’s the completed code: https://gist.github.com/4583089b8fdb8694fe30

          • Hi Bill,

            I’m looking to add a CPT to my main blog page in genesis (which is not the home page). When I try to add in is_page(‘archives’), the main blog archives page turns to to a 404 page.

            Here’s the code. Do you have any suggestions?

            function namespace_add_custom_types( $query ) {
            if( $query->is_main_query() || is_category() || is_tag() && empty( $query->query_vars['suppress_filters'] ) ) {
            $query->set( 'post_type', array(
            'post', 'tips', 'nav_menu_item'
            ));
            return $query;
            }
            }

            add_filter( ‘pre_get_posts’, ‘namespace_add_custom_types’ );

          • Bill Erickson says:

            A few issues:
            1. You need to make sure you’re always affecting the main query. You used an OR in there, not an AND.
            2. You never targeted the main blog page ( is_home() )
            3. I don’t think you want ‘nav_menu_item’ showing up in your blog archives.
            4. You don’t need to return the query. This is an action, not a filter.

            Here’s updated code: https://gist.github.com/billerickson/8919367

          • Hi Bill,

            That code snippet is so much cleaner! Thank you.

            However, it still didn’t add a CPT to the main blog page:
            http://www.landlordlogy.com/articles.

            I’m using genesis, and the Article page is using the page_blog.php page template. Too bad there isn’t a condition called “$query->is_blog()”.

            I’ve tried adding $query->is_page_tempate( ‘page_blog.php’), and $query->is_page( 1207 ), to your code, but when I do, the Articles page turns into a 404.

            Do you have any other advice?

            Thanks in advance. I really appreciate it!

          • Bill Erickson says:

            Never use the blog page template in Genesis. Just don’t do it.

            Go to Settings > Reading and set your blog page as the posts page. If your theme is written incorrectly (as many StudioPress themes are) and uses home.php as a widgetized front page, go into your theme files and rename that to front-page.php.

            I’ll be writing a blog post about things not to use in Genesis, and the Blog page template is #1.

        • Hi Bill,

          Thanks for the clarification! I got it working now. Why don’t the folks at studiopress follow this practice instead of making their themes with a blog template. Maybe you’ll answer that in your upcoming post.

          Thanks again!
          Lucas

  25. Don’t forget, $query->set( ‘meta_query’, …) will obliterate any meta query conditions that have already been set up by other plugins and filters. You need to get the current meta_query array (which may not be set at all, so needs initialising to an array if not) using $query->get( ‘meta_query’) then merge your conditions into that.

    – Jason

  26. Our home.php doesn’t require any default posts query. I’m a little lost on how to skip the main query entirely.

    So far the best I’ve been able to come up with is set posts_per_page to 1 (because if it is 0 it gets set back to the default). Any thoughts on how one might cancel the query from pre_get_posts?

    • Bill Erickson says:

      home.php is used for displaying your latest posts. If you don’t want to display posts, you shouldn’t be using home.php. If this is for the front page of your site, go to Settings > Reading and select a page to be on the homepage.

      If for some reason you must use home.php but you don’t want posts listed, find where the loop is being generated in that template file and remove it. In genesis, you’d accomplish this by adding the following to home.php: remove_action( 'genesis_loop', 'genesis_do_loop' );

      • I hear you about the static homepage option. The problem for us is that we still want the query to run for paged requests (e.g. /page/2).

        Here is what I have in a pre_get_posts filter. It more or less works:
        if ($query->is_main_query() && $query->is_home && !$query->is_paged) {
        $query->is_home = false;
        $query->is_page = true;
        $query->is_posts_page = false;
        $query->is_singular = true;
        $query->set(“page_id”, HOME_PAGE_ID); // constant set in wp-config.php
        $query->set(“post_type”, “page”);
        }

        Thanks for the genesis loop tip, I’ll give that shot.

        • Bill Erickson says:

          What are you using the paged requests for if you’re not displaying posts? In any case, those paged requests work on static pages as well.

  27. Hi Bill, when using the genesis loop, when a post is set as sticky, (for display on the homepage), the post also appears on page 2 (i.e. the posts overflowing from the front page onto subsequent pages).
    I’m thinking one way to remove the sticky from the overflow (page 2) listing, would be to assign it to a category–& then exclude that category from the standard genesis loop display.
    I writing to ask how I would do that–&/or if there’s a better way to achieve the same.
    Thanks,
    Simon

    • Bill Erickson says:

      You can use the ‘ignore_sticky_posts’ parameter to exclude them: http://codex.wordpress.org/Class_Reference/WP_Query#Sticky_Post_Parameters

      • Thanks Bill! Works perfect!
        Simon

      • Actually Bill, there is one other issue that’s arises from the use of:
        ‘ignore_sticky_posts’ => 1
        Because there’s a single sticky post at the top of the home page, (which the template layout being utilised) it means one extra post is present on the home page compared to the overflow pages–which means the layout is affected on page 2, 3, 4, etc.

        • Bill Erickson says:

          I don’t think there’s anything you can do about this. Why not just drop the sticky posts altogether? Or, add ignore_sticky_posts for all pages (I assume you’re only doing it for inner pages), then add a new custom query to the top that only runs on the first page and only displays the sticky post.

          • The homepage sticky’s such an integral part of the design–aesthetics wise. I need the single sticky to count for two posts. Is there a counter I can use to achieve this.

            What you’ve written (below) sounds like a possibility, (if I can get the sticky using it’s unique display formatting). How would I create/incorporate this query:
            ===
            add a new custom query to the top that only runs on the first page and only displays the sticky post
            ===

  28. Hi Bill! First, thank you so much for your tutorials on here, they have helped so much! I am learning php on the fly, and am trying to do something specific on my archive page using the grid loop. I believe I have to use a custom query and I’m clueless, so I’m here to beg for help lol!
    Basically, I would like an archive page that shows the title and featured image for each post, but I would like all posts displayed by category. I don’t know if I am just being a glutton for punishment, but I’m really intrigued to see if this is possible, and how.

    My archive page template is set up as follows:
    https://gist.github.com/4562711
    I have it set up so I choose the categories displayed on the page via the: “genesis_get_custom_field(‘query_args’); / custom field setting on the page.

    I have tried using the wp codex, and adding
    echo ” . get_cat_name( $cat_id ) . ”;
    to the query section, but it doesn’t work… maybe I am putting it in the wrong place?
    Just to reiterate, I am quite clueless (sorry!) but I’d like the archive page to be set up so that each category is displayed on the page, ALONG WITH every post (title & image) in that category. In short, multiple categories displayed, showing all posts in those categories, all on 1 handy-dandy page. I hope I explained that properly? Any advice would be much appreciated (or feel free to let me know if I really am being a glutton for punishment trying to do this!)
    Thanks so much.

    • Bill Erickson says:

      Your description sounds like you want to loop through all categories and display all of their posts, but the code you provided looks like you want to query a specific category.

      1. If you’re trying to display only the posts from a single category, and specifying that category in the backend using a custom field, your template would look like this: https://gist.github.com/4572940

      2. If you want a template file that loops through all categories, and for each category shows all their posts, your template file would look like this: https://gist.github.com/4572998

  29. Hi BIll,

    I used the pre_get_posts method to change the post_type used on my home page to a custom post type I have. This works great and avoids having to perform more queries on this page, however on other pages I use template files and would love to be able to target these pages in a similar way via functions.php and alter the main query as I have done with the home page.

    However, I tried simply changing if ( $query->is_main_query() && $query->is_home() ) { to if ( $query->is_main_query() && $query->is_page(ID) ) { but this doesn’t work. I’m sure there must be a way of using pre_get_posts on template pages, by altering the main query but I can’t work out how to do it.

    Any help would be appreciated as I really notice the difference in speed, thanks.

    • Bill Erickson says:

      I’ve never used it on template pages because in my experience, template pages are typically secondary queries, so I use WP_Query().

      In your template, put this:

      global $wp_query; print_r( $wp_query );

      That will tell you everything that’s currently in the query. You can then use the pre_get_posts method to remove everything you don’t want in there.

  30. I’m trying to remove certain categories from my RSS feed, can you tell me if I can use $query->set(‘cat’, ‘-99′); multiple times, or should I use an array?

    https://gist.github.com/4692710

    Thanks!

  31. Hi Bill and thanks for a great post! I was trying to change the number of posts shown in a particular category, but because of the posts_per_page pagination bug I simply couldn’t get it done, no matter what other possible solutions I’ve tried. Yours did the trick! Have a great one and thanks again!

  32. Hi Bill,
    Thank you for taking the time to write these posts — very helpfu!

    I am new to WP & Genesis, have some coding experience with minor PERL programming 10 years ago. I am having a challenge with how posts are handled at the index (home) versus the Archives. At the index I want one post, the most recent, to show in full. On the archives, I want multiple posts to show as an excerpt. The problem is WP & Genesis settings gives me either excerpts globally and/or 1 post globally (both home and archives). At WordCamp Atlanta, Travis Smith helped me with part of the problem (removed pagination on Archives — only one post listed at a time) but I later learned it didn’t address the entire problem. It is here:
    https://gist.github.com/wpsmith/5177901
    I know I’m on the right track and I need some direction, even how to go about it. Any assistance would be most appreciated! Also, starting PHP tutorials this afternoon!
    Thanks,
    Smythe

  33. Hello Bill,
    I was using fine the new WP_Query all the time, but then was told that it causes performance issues on the big project. Was suggested to use ‘pre_get_posts’ action. I browsed the web for quite long time, tried all the possible ways to use it in my case, but no success.
    In my case, every post has one of the categories (berries, veggies, fruit). Some of the posts have custom field ‘rating’ that contains some integer. I need to display the posts making sure that posts with higher rating are displayed first, and the are followed by all the rest (involves infinite scroll during it too).
    Here is what I tried: https://gist.github.com/emelyanenko/ff0f74c4293d58d43bd4
    Printed request looks like that:
    SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.ID = 227 AND wp_posts.post_type IN (‘post’, ‘page’) ORDER BY wp_posts.post_date DESC

    And it only returns me the content of that page with id = 227.

    How can I make it return me the other posts, not only the containing page? Ideally, I would need this action to be applied only to that particular query where I’m displaying the posts of the 3 mentioned above types.

    Thank you in advance

    • Bill Erickson says:

      You should only use pre_get_posts if you’re customizing the current page’s query. In your case, I think a custom query will be best. You’re trying to load these results on a page, so you need the page’s query to run first and then do your custom query inside of it.

      WP_Query actually does 4 queries to the database:

      1. Get the actual results of your query
      2. Figure out how many other posts match that query, for pagination purposes
      3. Get taxonomy terms for the current results
      4. Get metadata for the current results

      The first one is the important one. If you don’t need the other three, you can turn off the ones you don’t need like this: http://www.billerickson.net/code/improve-performance-of-wp_query/

      It looks like you’ll need all of them. You need the # of posts for pagination / infinite scroll, you need the categories, and you care about post meta (rating).

      Don’t worry so much about the performance of this query. Just make sure you have a caching plugin installed and then the query performance won’t matter much.

  34. I have been trying to query posts and hide all pages from it, but no success. Any help will be appreciated.

    https://gist.github.com/billerickson/5670429

    I am using the above code and it works fine, but it is querying also my pages which I don’t want to see.

  35. Hello,

    Is there anyway to exclude multiple categories?

    Thanks.

  36. Brian Novak says:

    Bill,
    Is there any way write the function to simply include one category and exclude all others? I have many categories that and will be adding new ones that I do not want to appear on the homepage feed.

    Thanks,
    Brian

  37. Nathaniel Ryan says:

    I’ve gone through these steps, but must be overlooking something very obvious, and hope you can assist. I want my category page(s) to sort A-Z.

    So using your example of Events:
    When I were to go to /events/ I would like the posts to be listed A-Z, as well if I have a child inside events called Sports or Free, then the A-Z sorting would still work on
    /events/sports/
    /events/free/

    I’m using Genesis, and feel like I should be able to go into my Child theme functions.php or using the Simple hooks plugin, I should be able to say orderby ‘title’ and order ‘ASC’ but I’m not having luck so far.

    Thanks so much-
    Nate

  38. Hello Bill,

    imagine you have two kind of “events” and want to use the same pre_get_post hook for both.
    But then, in one event custom post type you have defined a custom taxonomy “country” so you can query by $query->set( “country”, “uk”) but then, in the other event custom post type you havent defined any country to uk but still want to have a valid query.
    How would you do that?

    Since we are in the main_query we still dont know if “that query” will be empty so we cannot choose when to query by country uk or query by every country.

    I am doing it so far with another function.
    https://gist.github.com/antorome/b76cbdae202a4d22efe3#file-gistfile1-php

    In the example the tax is “seccion” and “mejores-ofertas”.

    so then back in the pre_get_posts I do
    if (cdb_productos_afiliacion(“event”))
    $query->set(“seccion”, “mejores-ofertas”)

    Do you know if there is anyway to add query->set(“country”, “uk”) directly in the main query so if the given custom post doesnt have any post with a country tax set to uk, it will query the default one???

    Thanks

    • Bill Erickson says:

      I don’t understand the question. Are you saying you have two different ‘event’ post types? Why?

      Why don’t you use the taxonomy archive for your country query? Go to /country/uk and you’ll see all events in that taxonomy term. Go to /events to see all events, regardless of country.

  39. Bill-

    This is so close to what I need to do on my custom blog page. Right now, I created a bare bones page_blog.php and added that to my child theme. Right now genesis is only pulling in the ‘post’ type. I’d like to to query ‘post’ and ‘podcast’.

    What should I add to my blog template to accomplish this? Thanks again for your such good resources.

    -Dan

    • Bill Erickson says:

      I do not recommend you use a custom page template like page_blog.php. You should be modifying the main WordPress query as described above.

      1. Delete your page_blog.php file
      2. Go to Settings > Reading and set your blog page as the one that displays your posts
      3. In functions.php, add this: https://gist.github.com/billerickson/9056496

  40. That would be okay, except in this theme, the front page- is a rather nice page (http://www.appendipity.com/pintercast/) that I don’t want to lose to a standard blog page.

    What this theme offers is a nice dynamic front page- then a blog page which pulls in podcasts and posts. Except that I can’t get the blog page to pull in CPT: ‘podcast’
    Make sense?

    -Dan

  41. Quick question.

    How do I exclude Genesis blog template from this grid loop.

    I can see this if( ! ( $query->is_home() || $query->is_archive() ) ) and from codex it says the “pre_get_posts action doesn’t work for Page requests.”

  42. Hi Bill,

    Thanks for the great tutorial.

    I’d like to sort a CPT Archive dynamically and wonder if pre_get_posts can be used in conjunction with a query string to accomplish this.

    Here’s a quick example, but I haven’t been able to get it to work: https://gist.github.com/aaroncolon/9244757

    Guessing it’s not possible or I’m missing something very obvious. Any help is greatly appreciated.

    Thanks,
    Aaron

  43. Is it possible to query posts based on the current date and then randomise the results using ‘&orderby=rand’.
    First I need to query the posts based on the current date and then apply ‘&orderby=rand’ to it? How do I do it?

  44. Hung Pham says:

    Hi,

    I want to combine the filters for all posts having publish status and all owned private posts. I am thinking to use
    - $query->set (‘post_status’, ‘publish’): for all published posts
    - $query->set(‘author’, $user_ID): for all owned posts

    But don’t know how to combine them together in one filter. Please advice.

    Thanks.

Join the Conversation

If you'd like to include code in your post, please post it to http://gist.github.com and include a link.