WP Tax Query Generator
Build a WordPress tax_query array: taxonomies, term fields, operators, child term handling and the relation between clauses, ready for WP_Query.
<?php
/**
* A tax_query over post.
*/
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'tax_query' => array(
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array_map( 'trim', explode( ',', 'news, updates' ) ),
'operator' => 'IN',
'include_children' => true,
),
),
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
the_title( '<h2>', '</h2>' );
}
// Always restore the global post after a custom loop.
wp_reset_postdata();
}
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Pick the taxonomies, how the terms are matched and what to do with child terms, and the generator writes the tax_query array with the relation and operators already in place.
How to use
- Add one clause per taxonomy. Two clauses with
ANDis the normal way to ask for posts in a category and with a tag. - Match by slug unless you have the IDs in hand. Slugs survive an export and an import; term IDs do not.
- Type the terms separated by commas. The generated code splits them, and casts them to integers when you match by ID.
- Turn off child terms when a parent should mean only itself. It is on by default in WordPress, which surprises people with hierarchical categories.
- Use
ANDas the operator when a post must carry every term, not just one of them.
Example
Posts in the news category, excluding anything tagged sponsored, with child categories left out:
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array_map( 'trim', explode( ',', 'news' ) ),
'operator' => 'IN',
// Off, so a parent term does not drag its children in.
'include_children' => false,
),
array(
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => array_map( 'trim', explode( ',', 'sponsored' ) ),
'operator' => 'NOT IN',
'include_children' => true,
),
),
);
Note the difference between the two operators: IN is any of these terms, AND is all of them.
Pitfalls
include_childrendefaults to true. A query for a parent category quietly returns everything underneath it.- Term IDs are not portable. A staging site and a live site rarely agree on them, which is why slug is the safer field.
INmeans any of the terms. Asking for posts carrying every term needs theANDoperator, which is a different thing from theANDrelation.NOT INwithinclude_childrenon also excludes the children. That is usually what you want and occasionally a surprise.field => namematches the display name, which editors rename. It breaks the day someone fixes a typo in a term.- Each clause joins the term relationship tables again. Five clauses on a large site is a slow query, cached or not.
term_taxonomy_iddoes not need a taxonomy in practice, but leaving the taxonomy out makes the query harder to read and to change later.- Mixing
tax_querywith thecategory_nameortagshorthands in the same query gives you two sets of conditions and confusing results. Pick one.
Compatibility
tax_query has been in WP_Query since WordPress 3.1, and nested clauses with their own relation since 4.1. The EXISTS and NOT EXISTS operators need WordPress 4.5 or later. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.
Frequently asked questions
Why am I getting posts from child categories?
include_children is true unless you say otherwise. Set it to false for a parent term that should mean only itself.Slug or term ID?
What is the difference between the AND relation and the AND operator?
Can I combine this with a meta query?
$args. They are joined with AND, and each adds its own joins, so keep an eye on the cost.How do I query for posts with no terms at all?
NOT EXISTS operator with just the taxonomy. It needs no terms and no field.