Complex/nested/relational tax_query and pre_get_posts troubles
I was recently tasked with modifying the admin search for a WordPress CPT to include a taxonomy dropdown filter that allowed the client to search a property by the number of bedrooms it contains as well as the type of home it was. The client wanted the search behavior to return properties with bedrooms greater than 1 if 1 was selected and greater than 2 if 2 was selected, etc.
During this attempt, I thought it would be an easy modification to the query within pre_get_posts by just building a brand new tax_query but I had a lot of trouble getting it to actually work with strange behaviors all over the place and many of my workarounds failing.
I tried nested complex relations for the tax query:
'tax_query' => array(
'relation' => 'AND',
array(
array(
'taxonomy' => 'home-type',
'terms' => ["single-family"],
'operator' => 'IN',
'field' => 'slug',
'include_children' => false
),
),
array(
'relation' => 'OR',
array(
'taxonomy' => 'beds',
'terms' => ["5", "6","7","8","9","10"],
'operator' => 'IN',
'field' => 'slug',
'include_children' => false
)
),
I tried non-complex tax_query modification:
'tax_query' => array(
'relation' => 'AND',
array(
array(
'taxonomy' => 'home-type',
'terms' => ["single-family"],
'operator' => 'IN',
'field' => 'slug',
'include_children' => false
),
array(
'taxonomy' => 'beds',
'terms' => ["5", "6","7","8","9","10"],
'operator' => 'IN',
'field' => 'slug',
'include_children' => false
)
)
),
And neither solved the issue. I tried, NOT IN, EXISTS, and AND operator approaches.
I found an older article from 2013 that suggested I back out the use of slug searches and move to term_taxonomy_id searches. This did not solve my issue. I tried switching to name searches, too, but that didn’t help solve the issue.
What did finally work was the unsetting of my taxonomy queries manually from $query->query and $query->query_vars within the reference query:
$tax_query_clone = clone $query->tax_query;
foreach ( $tax_query_clone->queries as $q ) {
unset($query->query[$q['taxonomy']]);
unset($query->query_vars[$q['taxonomy']]);
}
Why I believe this worked is because my search data was being passed as GET params and even though I was using this query data to build a completely new tax_query
inside of the pre_get_posts
hook, the presence of these lookups were also existing as main $query->query
search.
In other words, I believe $query
was performing two searches, one based on what was in $query->query
and another that supplemented the former through my new tax_query
.
UPDATE:
I discovered I had conflicting code that was modifying the query usingparse_query
. This code was related to prior taxonmy filter work that was implementing following this method
Here is a simplified look into the working method running in `pre_get_posts`
public static function modify_search_query( $query ) {
if ( !$query->is_main_query() || !is_admin() ) {
return;
}
if ( $query->get( 'post_type') !='property' ) {
return;
}
$tax_query_clone = clone $query->tax_query;
$tax_query_and = array('relation' => 'AND');
foreach ( $tax_query_clone->queries as $q ) {
unset($query->query[$q['taxonomy']]);
unset($query->query_vars[$q['taxonomy']]);
$q['include_children'] = false;
$q['field'] = 'slug';
$q['operator'] = 'IN';
switch($q['taxonomy']) {
case 'beds':
case 'baths':
$terms = get_terms($q['taxonomy'] , ['hide_empty' =>false]);
$int = (float) str_replace("-", "." , $q['terms'][0]);
$in = [];
foreach($terms as $t) {
if ( $int <= (float) $t->name ) {
$in[] = (string) $t->slug;
}
}
$tax_query_and[] = [
'taxonomy'=> $q['taxonomy'],
'field' => 'slug',
'terms'=> $in,
'operator' => "IN",
'include_children' => false
];
break;
}
}
$tax_query = [];
if (count($tax_query_and)>1) {
$tax_query = $tax_query_and;
}
$query->set('tax_query' , $tax_query);
echo "<pre style='display:none;'>";
echo "AND\r\n";
print_r($tax_query_and);
echo "TAX QUERY\r\n";
print_r($tax_query);
echo "QUERY\r\n";
print_r($query);
echo "</pre>";
}