First the code:
function itsme_better_editions( $query ) {
if ( $query->is_category() && $query->is_main_query() ) {
$query->set( 'post_type', array( 'post' ) );
// Get current tax query
$tax_query = $query->get( 'tax_query' );
$tax_query['relation'] = 'OR';
$tax_query[] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'intl',
'operator' => 'IN'
);
$query->set( 'tax_query', $tax_query );
}
return $query;
}
add_filter( 'pre_get_posts', 'itsme_better_editions' );
It all seems right to me, except $query->get( 'tax_query' );
seems to return empty or not an array, i.e. the following condition is returning true
:
if( !empty($tax_query) || is_array($tax_query) ) {
// whatever
}
This is breaking stuff, I later realized.
The right posts are being shown in the archive i.e. posts that belong to either the current category OR ‘International (intl)’ category are listed. Which is what I want.
But the term object is pointed to ‘intl’ category (the same happens in tag archives as well; instead of term object pointing to the current tag, it points to ‘intl’ category). For example, if I visit the ‘UK (uk)’ category archive, the displays ‘International’ instead of ‘UK’.
This is just one of the problems I’ve noticed so far; I don’t know what else is broken.
What’s wrong with the function?
PS: And because $query->get( 'tax_query' );
was returning empty or not an array, I had to manually fill it up like so:
/*
* Show posts assigned to 'International (intl)' Edition
* in all editions.
*/
function itsme_better_editions( $query ) {
if( $query->is_category() && $query->is_main_query() ) {
$query->set( 'post_type', array( 'post' ) );
// NOT WORKING!!!
//$tax_query = $query->get( 'tax_query' );
// Equivalent of original `$tax_query` START.
$get_original_category = get_query_var( 'category_name' );
$original_category = get_term_by( 'slug', $get_original_category, 'category' );
if( $original_category && !is_wp_error( $original_category ) ) {
$itsme_original_category = $get_original_category;
}
$tax_query[] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => $original_category,
'operator' => 'IN'
);
// Equivalent of original `$tax_query` END.
$tax_query['relation'] = 'OR';
$tax_query[] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'intl',
'operator' => 'IN'
);
$query->set( 'tax_query', $tax_query );
}
return $query;
}
add_filter( 'pre_get_posts', 'itsme_better_editions' );
OR simply replace the query like so:
/*
* Show posts assigned to 'International (intl)' Edition
* in all editions.
*/
function itsme_better_editions( $query ) {
if( $query->is_category() && $query->is_main_query() ) {
// Get original/actual category of the category archive
$get_original_category = get_query_var( 'category_name' );
$original_category = get_term_by( 'slug', $get_original_category, 'category' );
if( $original_category && !is_wp_error( $original_category ) ) {
$itsme_original_category = $get_original_category;
}
if( isset($itsme_original_category) ) {
$query->set( 'post_type', array( 'post' ) );
$query->set( "category_name", "{$itsme_original_category}, intl" );
}
}
}
add_action( 'pre_get_posts', 'itsme_better_editions' );
This works, but why should I do it this way? Isn’t $tax_query = $query->get( 'tax_query' );
supposed to return the original tax_query
?
How to reproduce the problem
1. Create two categories: UK (uk) and International (intl). Create 2 posts and assign them to ‘UK’ only; 1 under ‘International’ only.
Now, example.com/category/uk/
shows 2 posts; and example.com/category/intl/
shows 1.
2. Now add the first function (first code block) in your theme’s functions.php, and visit example.com/category/uk/
. You’ll see that the category name of the page (<?php single_cat_title(); ?>
) is shown as ‘International’. Why? If I am not wrong, because $query->get( ‘tax_query’ ); seems to return empty, not an array.
3. Replace the function in functions.php with the second or third functions above. Now everything should work as it’s supposed to.
PS: Yes, I did this test myself with the default theme (Twenty Thirteen) and it persists. So you’d be able to reproduce the problem just fine.
AFAIK
$query->get
for main query works only with public query vars, i.e. vars that can be triggered via url, but nothing prevents to directly access directly totax_query
property of query, but notice that it is an object, instance ofWP_Tax_Query
and the current queried taxonomy arguments are in thequeries
property of that object.Accessing to that property you avoid to run another query with
get_term_by
inside your function. As a side effect,single_cat_title
will print the correct title:Note that actually you are running the filter also on admin queries, if is not what you want add
&& ! is_admin()
inside firstif
conditional in function.PS: a tip: when using
'pre_get_posts'
you can useadd_action
instead ofadd_filter
and not return anything, because the query is passed as reference.$tax_query = $query->get( 'tax_query' );
will return the originaltax_query
if one is passed into the query. For example:However, if you dump
$wp_query
on a category archive, or run a similar test on the main query, there is notax_query
as you’ve already discovered. That is because for some of the default queries, theWP_Query
object, constructs thattax_query
from other query parameters long afterpre_get_posts
runs. That is why$tax_query = $query->get( 'tax_query' );
is empty.