Include and Exclude Taxonomies From Archives & Feeds Using ‘pre_get_posts’

What I am trying to do?

My blog uses a custom taxonomy called edition with terms like us-canada (6), eu (7) and india (8) — term slug (ID).

I want to make sure that posts not assigned to any specific ‘edition’ be shown under all the terms (i.e. if a post is not assigned to usa, europe or india, it’ll be shown in the archive pages of all these terms).

Read More

What I’ve tried?

Here’s the sample code pertaining to one of the terms, which should give you an idea of what I am exactly trying to do, and what I’d be doing wrong.

add_filter('pre_get_posts','better_editions_archive');

function better_editions_archive($query) {

    if ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) {
        $query->set( 'post_type', array( 'post' ) );

        $query->set( 'tax_query',
            array(
                'relation' => 'AND',
                array(
                    'taxonomy' => 'category',
                    'field' => 'id',
                    'terms' => array( 1, 2, 4, 5 )
                ),
                array(
                    'taxonomy' => 'edition',
                    'field' => 'id',
                    'terms' => array( 7, 8 ),
                    'operator' => 'NOT IN'
                )
            )
        );
    }

    return $query;
}

What’s wrong? The aforementioned code doesn’t work (others I’ve tried: code-1, code-2), it’s doesn’t change anything. There’s no debug error either.

So, what could I be doing wrong?

ALSO, to make sure that the changes also apply to feeds of these terms, I replaced the relevant line in the aforementioned code with this:

function better_editions_archive($query) {

    if ( ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) || ( $query->is_feed() && $query->is_tax( 'edition', 6 ) ) ) {

But that starts to redirect the terms’ feeds back to the terms’ archives. That is, with the function in place, example.com/edition/usa/feed/ redirects back to example.com/edition/usa/.

Again, I’ve no clue as to what I’d be doing wrong.

UPDATE: What’s worked for me? (But…)

add_filter( 'pre_get_posts', 'better_editions_archive' );

function better_editions_archive( $query ) {
    if ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) {

        $args = array(
            'post_type' => 'post',

            'tax_query' => array(
                'relation' => 'AND',
                array(
                    'taxonomy' => 'category',
                    'field' => 'id',
                    'terms' => array( 1, 2, 4, 5 )
                ),
                array(
                    'taxonomy' => 'edition',
                    'field' => 'id',
                    'terms' => array( 7, 8 ),
                    'operator' => 'NOT IN'
                )
            )
        );

        $query->query_vars = $args;
    }

    return $query;
}

It works, but the problem is, I’d an extended chat discussion with a knowledgeable WordPress developer, and he told me this (you can follow the full conversation here, but it’s too lengthy):

And you’re then assigning this array as the query_vars. Now the query_vars is a pretty large object. And you’re basically overwriting the data in there and just adding your custom ones. This means that you ‘unset’ everything that is added per default.

He pretty much advised against using this solution, and instead go with the $query->set(); method.

But as you can see far above, I couldn’t get the other one to work. So I am here to see if anyone can tell me what I am doing wrong, in a less-technical-speak.

Related posts

Leave a Reply

2 comments

  1. I’ll take another shot.

    The following should modify the main query, such that it will include in its loop any posts that belong to no term of the Edition custom taxonomy.

    add_filter('pre_get_posts','better_editions_archive');
    
    function better_editions_archive( $query ) {
    
        if ( $query->is_tax( 'edition' ) && $query->is_main_query() ) {
            $terms = get_terms( 'edition', array( 'fields' => 'ids' ) );
            $query->set( 'post_type', array( 'post' ) );
            $query->set( 'tax_query', array(
                'relation' => 'OR',
                array(
                    'taxonomy' => 'edition',
                    'field' => 'id',
                    'terms' => $terms,
                    'operator' => 'NOT IN'
                )
            ) );
        }
    
        return $query;
    }
    
  2. Note to self: @vancoder’s answer is basically a better (& automated) version of this:

    add_filter('pre_get_posts','better_editions_archive');
    
    function better_editions_archive($query) {
    
        if ( $query->is_tax( 'edition') && $query->is_main_query() ) {
    
            $query->set( 'post_type', array( 'post' ) );
    
            $query->set( 'tax_query',
                array(
                    array(
                        'taxonomy' => 'edition',
                        'field' => 'id',
                        'terms' => array( 6, 7, 8 ),
                        'operator' => 'NOT IN'
                    )
                )
            );
    
        }
    
        return $query;
    }
    

    (What the code says) If it’s the archive for any term that belongs to the custom taxonomy ‘edition‘, then…

    1. The code doesn’t mess with the default function of the terms’ archive pages, i.e., posts assigned to a term of the custom taxonomy ‘edition‘ will (as usual) be shown in the relevant term’s archive.

    2. But if a post is not assigned to any of the given custom taxonomy’s terms (see 'terms' => array( 6, 7, 8 ) and 'operator' => 'NOT IN'), then show such posts in the archive pages of all the terms of the custom taxonomy.

    3. The code also affects the feeds of the said terms, and therefore, the feeds mirror the content of archive pages, which is just what I need. In case you don’t want the feeds to be affected i.e. if you want the changes to only apply to the archive pages replace the IF with this:

      if ( $query->is_tax( 'edition') && $query->is_main_query() && ! $query->is_feed() ) {