Using WordPress to query multiple taxonomies
I recently was working on a project that required a page to show the content from blog post that belonged to a specific category, as well as some custom post types that belonged to a custom taxonomy. My initial reaction was that I would need to write a custom SQL query to do this, but upon a bit more investigation this is totally possible with a standard WordPress query. Well standard might be a stretch but see what I mean.
Query multiple post types
If all you need to do is query multiple custom posts you can do that with plain old query_posts.
But what if you need to grab posts only from one category and from specific taxonomies. Then you would have to get a bit creative because the above code could not handle that, instead you need to turn to tax_query.
Querying multiple taxonomies
tax_query is a new query function, which is really and array of arrays that specifies what to pull out of the database. Since that is a bit hard to understand by itself an example is probably helpful. Lets say you want to pull all posts in a category of “cheese” and all the custom post types “dairy” with a taxonomy of “yogurt” you could write something like this:
One thing is that this will not work! You see by default the relationship in WordPress is “AND” so all these parameters have to be true. Instead the correct version is to change the relationship to an “OR” , you can do that like this:
This simple change is immensely powerful, because you are not limited to categories or post types, you can also add tags.
$myquery['tax_query'] = array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'terms' => array('cheese'),
'field' => 'slug',
),
array(
'taxonomy' => 'dairy',
'terms' => array('yogurt'),
'field' => 'slug',
),
array(
'taxonomy' => 'post_tag',
'terms' => array('bar'),
'field' => 'slug',
),
);
query_posts($myquery);
or how about a negative match instead, then you would use the operator parameter:
$myquery['tax_query'] = array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'terms' => array('cheese'),
'field' => 'slug',
),
array(
'taxonomy' => 'dairy',
'terms' => array('yogurt'),
'field' => 'slug',
),
array(
'taxonomy' => 'post_tag',
'terms' => array('bar'),
'field' => 'slug',
'operator' => 'NOT IN',
),
);
query_posts($myquery);
What about pagination
So after finding the right query, I was a bit miffed when I discovered that WordPress would actually not support pagination for this method. The thing is that with WordPress as soon as you run a $query_posts it will overwrite all the original page code that would be there by default (the $query_string). Without that information WordPress cannot paginate correctly. So what you want to do is retain some of this information, I did it with wp_parse_args():
global $query_string;
$myquery = wp_parse_args($query_string);
$myquery = array(
'paged'=>$paged,
'numberposts'=>-1,
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'terms' => array('blog'),
'field' => 'slug',
),
array(
'taxonomy' => 'custom_post_type_1',
'terms' => array('custom_taxonomy_1'),
'field' => 'slug',
),
array(
'taxonomy' => 'custom_post_type_2',
'terms' => array('custom_taxonomy_2'),
'field' => 'slug',
),
),
);
query_posts($myquery);
Hope this helps you with some future projects! If you would like to add to the conversation please use the comments 🙂