template_include for search.php makes WordPress think its on the home page

I have a custom tag-search feature, which allows people to search multiple tags to return relevant posts. Say they want to see posts that are tagged both ‘WordPress’ and ‘Tutorials’. I’m using the template_include filter, like so:

public static function template_redirect( $template ) {
    if( isset($_GET['tag-search']) )
    {
        $template = locate_template( array(
            'search.php',
            'archive.php',
            'index.php'
        ));
    }
    return $template;
}

the tag-search variable holds the name of the taxonomy, I check if it is set, thus indicating a search is being performed.

At this point template resolves to search.php, like it should. So that’s good.

In the pre_get_posts action I add a tax_query to modify the returned posts to just the selected tags, and I set is_search to true, and is_home to false. Here is my pre_get_posts action:

public static function pre_get_posts($query) {
    if( !isset($_GET['tag-search']) || !isset($_GET['terms'])  ) return $query;

    if( strtolower( $_GET['method'] ) == 'and' )
    {
        $query->set( 'tax_query', array(
            array(
                'taxonomy' => $_GET['tag-search'],
                'field' => 'slug',
                'terms' => explode(',', $_GET['terms']),
                'operator' => 'AND'
            )
        ) );
    }
    else
    {
        $query->set( 'tax_query', array(
            array(
                'taxonomy' => $_GET['tag-search'],
                'field' => 'slug',
                'terms' => explode(',', $_GET['terms'])
            )
        ) );
    }
    $query->is_search = true;
    $query->is_home = false;
    return $query;
}

However, when I perform the search, the body class is ‘home’, and all of the is_home and is_front_page conditionals are true, which they shouldn’t be, cause I just specifically stated I want this to be a search template right?

I know this can be done – when I add &s to the query string it works as it should – though it’s a bit ugly that way. Any insights?

Solutions Collecting From Web of "template_include for search.php makes WordPress think its on the home page"

Your problem is that you are not resetting all the needed query variables, like WP_Query::$is_page, WP_Query::$is_single and WP_Query::$is_archive.

Also note that 'pre_get_posts' is fired for all queries, the main and the secondaries, so you should check that you are working on the main query.

Finally, when you get data from $_GETyou should sanitize it before to use it, a good way is to use filter_input() or filter_input_array().

Your method should appear something like this:

public static function pre_get_posts($query)
{
    $data = filter_input_array(INPUT_GET, array(
        'tag-search' => FILTER_SANITIZE_STRING,
        'terms'      => FILTER_SANITIZE_STRING,
        'method'     => FILTER_SANITIZE_STRING,
    ));
    if( $query->is_main_query() && !empty($data['tag-search']) && !empty($data['terms']) ) {
        $query->init(); // this resets ALL vars
        $query->is_search = true;
        // set tax query array
        $tax_query = array(
            'taxonomy' => $data['tag-search'],
            'field'    => 'slug',
            'terms'    => explode(',', $data['terms']),
        );
        // is AND method?
        if( strtolower( $data['method'] ) === 'and' ) {
            $tax_query['operator'] = 'AND';
        }
        // set tax query
        $query->set( 'tax_query', array($tax_query) );
    }
}

Doing so you do not need to filter the template (so you can remove your template_redirect() method): search.php will be loaded by WordPress because it will correctly recognize the query as a search.


PS: 'pre_get_post' is an action, not a filter, so you don’t need to return the query neither if you modify it.

Try the following:

public static function pre_get_posts($query) {
    if( !isset($_GET['tag-search']) || !isset($_GET['terms'])  ) return $query;
        if ( ! is_admin() && $query->is_main_query() ) {
        if( strtolower( $_GET['method'] ) == 'and' )
        {
            $query->set( 'tax_query', array(
                array(
                    'taxonomy' => $_GET['tag-search'],
                    'field' => 'slug',
                    'terms' => explode(',', $_GET['terms']),
                    'operator' => 'AND'
                )
            ) );
        }
        else
        {
            $query->set( 'tax_query', array(
                array(
                    'taxonomy' => $_GET['tag-search'],
                    'field' => 'slug',
                    'terms' => explode(',', $_GET['terms'])
                )
            ) );
        }
        $query->set('s', '' );
        $query->is_search = true;
        $query->is_home = false;
    }
    return $query;
}

I added some extra checks and the ‘s’ in a less ugly way