I have been stuck on a particular problem for a while now and have yet to find a way around it. I have custom widgets and for each widget I have added the ability to filter posts based on category AND/OR tags (this functionality works). I am now trying to expand on that by including in each widget a custom field where you can enter the ID of a category you wish to exclude (the custom field is already in the widget and stores user input).

Let me show you what I have so far to help understand:

        $args = array(
            'post_type'         => 'post',
            'post_status'       => 'publish',
            'posts_per_page'    => $num_posts,
            'cat'               => -5,
            'tax_query'         => array(
                'relation'      => 'OR',
                array(
                    'taxonomy'  => 'category',
                    'field'     => 'id',
                    'terms'     => $category,
                ),
                array(
                    'taxonomy'  => 'post_tag',
                    'field'     => 'id',
                    'terms'     => $tags
                ),
            ),
            'offset'            => 1,
        );

As you can see I am taking in two arrays of input for both $category and $tags (this is working fine), but what does not seem to be working is:

'cat'               => -5,

If I remove the tax_query array completely, the exclude category works perfectly fine, so it appears that tax_query is overriding the exclude?

If someone could point me in the right direct, that would be great 🙂

Thanks!

2 Answers
2

The cat-parameter is not overwritten by tax_query. Instead, it is added to tax_query. Thus, using 'cat' => -5, the following array is appended to tax_query:

(
    [taxonomy] => category
    [terms] => Array
        (
            [0] => 5
        )

    [include_children] => 1
    [field] => term_id
    [operator] => NOT IN
)

The resulting taxonomy query is built up of three different clauses with the OR-operator. Thus, in your case, adding 'cat' => -5 simply adds the array described above to tax_query, resulting in the query returning that posts that are either

  • not in category 5,
  • in any of the categories in $category, or
  • in any of the tags in $tags

Nested tax_query

There is no way to use nested conditionals in tax_query. To achieve the conditional you are looking for (posts in a category from $category or having a tag from tags, but in all cases not being in category 5), you would have to hook into posts_where and possibly posts_join.

For anyone interested, the same goes for meta_query: this does not support nested queries either.

EDIT

Using posts_where

In response to your comment: to achieve this using posts_where, you will want to apply the filter to just the one query for which you want to exclude a category. To do so, add the filter before creating your query, and remove it afterwards:

add_filter( 'posts_where', 'myplugin_posts_where' );
$query = new WP_Query( $args );
remove_filter( 'posts_where', 'myplugin_posts_where' );

EDIT

Constructing the additional WHERE-clause

Using get_tax_sql to construct the additional WHERE-clause is useful as you do not have to construct your SQL manually, and the term IDs are automatically converted to term-taxonomy IDs.

$clauses = get_tax_sql( array(
    array(
        'taxonomy' => 'category',
        'field' => 'id',
        'terms' => 2,
        'operator' => 'NOT IN'
    )
), $wpdb->posts, 'ID' );

$where .= $clauses['where'];

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *