WordPress Sub-queries – The Many Options and Best Practices

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 is_tag(), is_single() etc.

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…

Option 1:

Pros

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.

Cons

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 false

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.

Other Notes

Calling wp_reset_query() at the end resets the scope back to the view’s original query.

Option 2:

Pros

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.

Cons

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_action() and 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_html(), 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…

Option 3:

Pros

Allows the use of template tags like the_content() without affecting the conditionals like is_tag().

Cons

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.

Other Notes

$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.

Digging Deeper

For those wanting to know more, I recommend having a peak at the get_posts() and 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 (is_home()).

If you’re still thirsty then venture into the wp_reset_postdata() and 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 wp_reset_query().