There are a lot of ways to query posts in WordPress and a lot of parameters you can use for pulling in just the data you want. For example, you can easily pull in just posts that are in category X, Y, or Z, and you can also just as easily pull in only posts that have a particular meta field set to some value or other. This tutorial video is going to focus on demonstrating the correct way to modify queries and also show how to easily query posts based on meta fields.
One of the worst mistakes that many, many developers make (I’ve been guilty of it countless times) is using query_posts() to modify the main query. One particular example I can think of is using query_posts() in search.php to modify the search results.
Another “crime” that a lot of developers, myself included, are guilty of is modifying queries with a disregard for other queries that might be running on the page. For example, a lot of theme developers like to add an option to their admins for controlling the number of posts displayed on archive pages. A lot of times what happens here is the custom posts_per_page parameter the developer sets overrides ALL queries on the page. I’ve seen this happen several times with my Sugar FAQs plugin; the posts_per_page parameter set by the theme overrides the posts_per_page set in the get_posts() query for the FAQs, so instead of showing 15 questions, the short code only shows 5.
There are many other “crimes” that get committed when queries are involved, but we don’t need to go through. Suffice it to say that the correct way to modify queries anywhere on your WordPress site is through the pre_get_posts action hook (also available as a filter).
Discussed in depth in the video, the pre_get_posts action allows us to easily modify queries anywhere on the site. Here is a quick example of how to force the front page to only show posts that have a meta key called “ecpt_color” that is set to a value of “green”.
1 2 3 4 5 6 7 8 9 10 11 | function pw_filter_query( $query ) { if( is_front_page() && is_main_query() && $query->query_vars['post_type'] != 'nav_menu_item' ) { $query->set('meta_key', 'ecpt_color'); $query->set('meta_value', 'green'); } } add_action('pre_get_posts', 'pw_filter_query', 9999); |
Join Now for $6

I’m so glad you published this tut, exactly what I was looking for. Now all I’ll do is hook-up a little front-end form for users to choose their filter options and I’ll be in good shape! Looks like pre_get_posts() doesn’t have sort options – so I may need to dig into that a bit more (it’s saying use query_posts() for that). Thank you again very much!
You mean sort options for setting the order items are displayed? Or what they are sorted by?
See Paul’s comment below. Is that what you’re referring to?
I believe so – ill give that a shot — I need to sort by a meta value as well, so I’ll take another look at how that is done in the tut. Thanks!
You shouldn’t have any problems sorting by a meta value. Use sort=meta_key and orderby=meta_value.
If your meta values are numeric, then use orderby=meta_valu_num
so do yo uknow the reason why on the codex, one example uses query->set and the other changes the query_vars object?
for example to change the post order, I used this
https://gist.github.com/2995754
I really don’t know. As far as I know, they do exactly the same thing.
Exploring Core a bit I think I’ve found the answer.
$query->set() is defined as this:
So that means that $query->set() is doing nothing more than assigning what you pass to the query_vars array.
We could use $query->get() for doing the same thing as $query->query_vars['parameter'] with $query-&t;get(), which is defined as this:
So no, there is really no difference at all.
Aside from the two methods mentioned, there’s actually a third function to do the same thing: set_query_var( $var, $value ).
Oh that’s cool. It’s the companion to get_query_var(). That’s nice because you can use it outside of pre_get_posts, though you’d have to be careful with that.
That’s what I was thinking regarding using it outside of pre_get_posts. I was just told that the WP.com theme team recommends using the set() method over the set_query_var() function within pre_get_posts, which makes sense since you have access directly to the object reference. If it’s just to save the overhead of the function call, it seems like the direct assignment method would be preferable.
Brady Vercher mentioned
set_query_var. You should avoid using this as it applies only the global$wp_query.pre_get_postsis applied to everyWP_Query(and sometimesget_posts). So if you only want to alter the main query, ensure to check theis_main_querymethod. Using$query->set()has the advantage of modifying secondary queries too (if this is desired).@Pippin – typo in the code?
is_main_queryshould be$query->is_main_queryYeah, you’ll need to be very careful with set_query_var().
No, I don’t think that’s incorrect. You only have to use $query->is_main_query when using the pre_get_posts filter. Then action accepts all regular conditional tags. I could be wrong though.
So you need to use the
$query->is_main_queryinpw_filter_query? The method$query->is_main_querychecks if$querymatches the original query:$wp_the_query. The functionis_main_querychecks if the global$wp_querymatches the original query$wp_the_query. (So, should return false ifquery_postis used and not reset after wards).For instance, in your example with
pw_filter_querythat should effect all secondary queries on the front page too.Hey Pippin,
Just doing some more testing — running the following: http://pastebin.com/k6sLishJ I’m using this to set a front-end URL parameter equal to ‘post_type=THE_POST_TYPE’ and it seems to work for everything but pages. One more interesting aspect is that if you look at the print_r() of $query->query_vars that I have output in that function, you’ll see that when you set the URL paremeter to &post_type=page (like http://localhost/wordpress/?s=test&submit=Search&post_type=page for example) the post_type query variable is missing.
Right now what I have to do as a workaround is http://pastebin.com/c1mUNHKu which seems kinda silly – any thoughts? Thanks!
That first code is not going to do anything. Your $query->set() function is doing nothing more than setting the post_type var to itself, the existing post type var. You should just use the $_GET method in the second one. No need for the If/ELSE, just use the code in the ELSE.
Appreciate it Pippin – it looks like when I use the following http://pastebin.com/U1Rjs36X everything works as expected (even pages), but does show the following error when I filter by pages ‘Undefined index: post_type’ which goes back to the original issue – seems kinda weird that when I set it to pages it would show that error. Thanks.
I think I know why. You are using add_filter() instead of add_action(). The add_filter() method of using pre_get_posts requires that you return the $query;
Bleh – think I’m getting the same thing after trying a few variations. If you just so happen want to nail this in the ground, hit me up on skype
Hey Pippin,
I’ve been trying this technique on my site which uses your Events plugin. Basically in the events archive I just want to show future events.
I’ve tried this but a little lost now…
https://gist.github.com/3018263
Cheers,
Steve
Pippin – Actually don’t waste your time. I should have looked in your code and worked it out from there. Here’s the complete query for anybody who wants run a pre_get_posts meta query.
https://gist.github.com/3018263
It’s for Pippin’s Sugar Events Calendar too
Yep that should work just fine
nice tutorial, i did this recently instead of doing custom queries
That’s how they should be done
Thanks Chris!