Using pre_get_posts to rewrite search query to display posts from multiple taxonomies

This seems like it should be straightforward, but I’m stuck on it!

If a user searches for ‘alice’, I would like to show them all posts with the custom taxonomy terms of ‘bob’ and ‘carol’, regardless of if those posts contain the string ‘alice’ in them or not.

Read More

I’m able to set the query’s tax_query so that it only shows posts with ‘bob’ and ‘carol’ as terms, but I can’t reset the actual search query so that it is showing all posts, not just posts with ‘alice’ in them.

This is the filter I’m using to change the tax_query:

if ( !$query->is_search )
    return $query;

$taxquery = array(
    'relation' => 'OR',
    array(
        'taxonomy' => 'people',
        'field' => 'slug',
        'terms' => array( 'bob' ),
    ),
    array(
        'taxonomy' => 'people',
        'field' => 'slug',
        'terms' => array( 'carol' ),
    )
);

$query->set( 'tax_query', $taxquery );
return $query;

but using this to set the search query doesn’t work:

$queryvars = array(
        's' => ''
);
$query->set( 'query_vars', $queryvars );

Related posts

Leave a Reply

1 comment

  1. It does’t work because you can’t use set to change all query vars. Simplest way to do the trick is set 's' to an empty string:

    add_action( 'pre_get_posts', function( $query ) {
    
      if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
        $taxquery = array( ... );
        $query->set( 'tax_query', $taxquery );
        $query->set('s', '' );
      }
    
    });
    

    In this way, WordPress will still consider the request a search, so the search.php template will be used to show results and if you call is_search() the result will be TRUE.

    If you want that WordPress will not consider anymore the request a search, you need to also manually unset $query->query_vars['s'] and set $query->is_search to false:

    add_action( 'pre_get_posts', function( $query ) {
    
      if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
        $taxquery = array( ... );
        $query->set( 'tax_query', $taxquery );
        $query->set('s', '' );
        unset( $query->query['s'] );
        $query->is_search = FALSE;
      }
    
    });
    

    Doing that, WordPress will not anymore consider the request a search, and will use index.php as template.

    If you want to use another template, e.g. taxonomy.php you need to use 'template_include' filter:

    add_action( 'pre_get_posts', function( $query ) {
    
      if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
        $taxquery = array( ... );
        $query->set( 'tax_query', $taxquery );
        $query->set('s', '' );
        unset( $query->query['s'] );
        $query->is_search = FALSE;
        add_filter( 'template_include', function() {
          return locate_template( 'taxonomy.php' )
        }, 0 );
      }
    
    });