0

Is it possible to modify a WP_Query object to add a tax_query?

That is -- I'm able to add a tax_query when instantiating a WP_Query object

$q = new WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
        array(
            'taxonomy' => 'post_format',
            'field' => 'slug',
            'terms' => array(
                'post-format-link',
            ),
            'operator' => 'IN'
        )
    )
));

The above query only returns posts with a link format.

However, if I have an already instantiated WP_Query object, I'm not able to set a tax_query. If I try it like this

$q = new WP_Query(['post_type'=>'post']);
$q->set('tax_query', array(
    array(
        'taxonomy' => 'post_format',
        'field' => 'slug',
        'terms' => array(
            'post-format-link',
        ),
        'operator' => 'IN'
    )
));

My query returns all post types, not just link posts.

Is this possible to do? If so, am I using set wrong, or do tax queries need to be set differently?

If it helps, the larger context is I'm trying to change a query object in the pre_get_posts action-hook. I've been able to use the pre_get_posts hook to add a category filter to a query

    add_action('pre_get_posts', function($query) {
        if(!$query->is_main_query() || is_admin() || !is_front_page()) {
            return;
        }
        $term = get_category_by_slug('my-slug');
        if(!$term || !is_numeric($term->term_id)) {
            return;
        }
        $query->set('category__not_in', $term->term_id);
    });

With the above in place my front page omits posts with the my-slug category.

However, if I try a similar thing with a tax_query, the front page is not restricted to link posts. The following

    add_action('pre_get_posts', function($query) {
        if(!$query->is_main_query() || is_admin() || !is_front_page()) {
            return;
        }
        // per an answer below, I've tried things both with and without
        // this next line and have not had luck in getting my tax_query 
        // to apply to the WP_Query
        // $tax_query = $query->get( 'tax_query', [] );        
        
        $query->set('tax_query',
            array(
                'taxonomy' => 'post_format',
                'field' => 'slug',
                'terms' => array(
                    'post-format-link',
                ),
                'operator' => 'IN'
            )
        );
    });

has no effect on the global query for the front page.

2

If you instantiate WP_Query with arguments, as in your second example, then the query is performed right away, and setting new arguments will not change the results.

$q = new WP_Query( [ 'post_type' => 'post' ] );

$q->set( 'tax_query', [] ); // Too late.

If you instantiate WP_Query without arguments, like this:

$q = new WP_Query();

Then the query is not performed until $q->get_posts() is run. This means that you can set query vars after the object is created:

$q = new WP_Query();

$q->set( 'post_type', 'post' );
$q->set( 'tax_query', [] );

$q->get_posts();

while ( $q->have_posts() ) {
    // etc..
}

There's nothing special about tax_query when it comes to setting arguments like this.

The pre_get_posts hook is fired inside WP_Query::get_posts(), before the query itself is performed, so in that hook there's no reason you can't set tax_query:

add_action(
    'pre_get_posts',
    function( $query ) {
        $query->set( 'tax_query', [] );
    }
);

The one thing that might trip you up is that any given query might already have a tax_query, and by setting it yourself you could be removing that original query. The way around this is to retrieve the original query, and then add yours.

add_action(
    'pre_get_posts',
    function( $query ) {
        $tax_query = $query->get( 'tax_query', [] ); // Make sure we have an array as the default.

        $new_tax_query = [
            [
                'taxonomy' => 'post_format',
                'field'    => 'slug',
                'operator' => 'IN',
                'terms'    => [ 'post-format-link' ],
            ],
            $tax_query,
        ];

        $query->set( 'tax_query', $new_tax_query );
    }
);
1
  • Thank you Jacob -- I learned things -- in particular that creating a WP_Query object with/without args can affect when the actual query runs. After taking your post into consideration and re-looking at my own code, the problem is/was that the tax_query value I was setting was not correctly formatted. I was missing an outer array. I've updated the question to make that clearer for the next person to stumble across this. Thanks again for your help.
    – Alan Storm
    Sep 9 at 21:24

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.