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

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 );
  }

});

Leave a Comment