3

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.

  • It would be helpful if you provided example reproducible on generic install / official test data. There are couple pages worth of code here which are specific to your setup and will be hard to untangle if they are failing because of that without having your exact setup. – Rarst Nov 22 '13 at 10:20
6
+50

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 to tax_query property of query, but notice that it is an object, instance of WP_Tax_Query and the current queried taxonomy arguments are in the queries 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:

function itsme_better_editions( $query ) {

  if ( $query->is_category() && $query->is_main_query() ) {

    $query->set( 'post_type', array( 'post' ) );

    $tax_query_obj = clone $query->tax_query;

    $tax_query_obj->queries[] = array(
      'taxonomy' => 'category',
      'field' => 'slug',
      'terms' => 'intl',
      'operator' => 'IN'
    );

    $tax_query = array('relation' => 'OR');

    foreach ( $tax_query_obj->queries as $q ) {
      $tax_query[] = $q;
    }

    $query->set('tax_query', $tax_query);
  }

}

add_action( 'pre_get_posts', 'itsme_better_editions' );

Note that actually you are running the filter also on admin queries, if is not what you want add && ! is_admin() inside first if conditional in function.

PS: a tip: when using 'pre_get_posts' you can use add_action instead of add_filter and not return anything, because the query is passed as reference.

2

$tax_query = $query->get( 'tax_query' ); will return the original tax_query if one is passed into the query. For example:

function itsme_better_editions( $query ) {
  $query->set( 'post_type', array( 'post' ) );

  // Get current tax query
  $tax_query = $query->get( 'tax_query' );
  var_dump($tax_query); die;
}
add_action( 'pre_get_posts', 'itsme_better_editions' );

$t = new WP_Query(
  array(
    'post_type' => 'post',
    'tax_query' => array(
      array(
        'taxonomy' => 'category',
        'field' => 'slug',
        'terms' => 'intl',
        'operator' => 'IN'
      )
    )
  )
);

However, if you dump $wp_query on a category archive, or run a similar test on the main query, there is no tax_query as you've already discovered. That is because for some of the default queries, the WP_Query object, constructs that tax_query from other query parameters long after pre_get_posts runs. That is why $tax_query = $query->get( 'tax_query' ); is empty.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.