Recently, while doing the new build for www.nickfleming.com, I had a situation where I wanted to show excerpts for my related post stubs on the Tag templates but not on the Single templates. Doing an
is_tag() strangely returned
true for all contexts.
Upon investigating I realised I was overriding the global
$wp_query and with it, the context of the view and therefore the values of the conditional tags such as
I decided to get my head around WordPress sub-queries once and for all (and tell the world!)…
Basically, you have three options when you want to do a subquery in WordPress. Each has different advantages and disadvantages. The best choice depends on the situation…
query_posts( $args );
while ( have_posts() ): the_post();
// stuff including the_title(), the_excerpt(), etc.
Simplest approach. Template tags (eg
the_title()) still work as expected and you can take advantage of the fact that they are filtered whereas accessing the post data directly (see option 2) skips the filters.
Replaces the global query object and the corresponding conditional tags like
is_home(). If your subquery looks like a Tag template query then you might suddenly find that
is_tag() == true for your homepage while
is_home() gives you
When to Use
Use this when you want to replace the default main query for the template, or when simplicity is important and conditional tags aren’t an issue.
Calling wp_reset_query() at the end resets the scope back to the view’s original query.
$some_posts = get_posts( $args );
foreach ( $some_posts as $a_post ):
// output via echo $a_post['post_title'], etc.
Cleanest approach. No globals are altered so all conditional tags work as expected. No need to reset anything at the end. With this approach it’s possible to render multiple queries at the same time.
You’ll miss a lot of the ease and detailed functionality of the template tags. If you want the content to be handled via plugins, you’ll need to call the appropriate
do_filter() functions before outputting. This can get messy;
the_content(), for example, has a “the_content” filter which it applies after calling
get_the_content() which also runs a filter namesake.
You may also need to escape (
esc_attr()) data before output.
When to Use
This method is useful for more advanced techniques such as if you want to ensure that the data remains untouched by all the plugins or if you want to merge the results of multiple queries, possibly with different (custom) posts types, into a single complex output. I’d advise only using this if you know you need it…
$my_subquery = new WP_Query( $args );
while( $my_subquery->have_posts() ): $my_subquery->the_post();
// output via the_title(), the_content(), etc.
Allows the use of template tags like
the_content() without affecting the conditionals like
Still affects the global
$post which could potentially confuse some plugins(?). I haven’t seen any issues yet…
When to Use
Default to this technique, especially if you are experiencing issues with Option #1.
$my_subquery is specified as global so subtemplates invoked via
get_template_part() can access it (be sure to include the
global $my_subquery line in the subtemplates too). Note, if you are using
include() directly (naughty!) you need not make it global as the included file will share the invoker’s local variable scope.
For those wanting to know more, I recommend having a peak at the
query_posts() functions to learn more about how WordPress handles it’s globals behind the scenes as will the template tags (
the_excerpt()) and conditional tags (
If you’re still thirsty then venture into the
wp_reset_query(). You’ll discover the global vars
$post along with
$wp_query and it’s esteemed cousin
$wp_the_query. The later is a sort of safety net and contains the original query invoked by the requested URL, set by the
get_posts() method on the main workhorse class
WP. It’s what the global
$wp_query resets to on a call to