WordPress Search – display taxonomy terms in results

I just need to be able to spit out the term as a result itself, with
a link to the term (and be counted in the search count query). So if a
search matches a taxonomy term that should come first, followed by the
other results in whatever order.

WP_Query::$posts are WP_Post objects and can’t include (or be mixed with) term/WP_Term objects.

However, there’s a way to achieve what you want — use get_terms() to search for the taxonomy terms and modify the WP_Query‘s posts_per_page and offset parameters to make the matched terms as if they’re part of the actual search results (which are posts).

Note though, for now, get_terms()/WP_Term_Query supports single search keywords only — i.e. foo, bar is treated as one keyword — in WP_Query, that would be two keywords (foo and bar).

The Code/Steps

In functions.php:

Add this:

function wpse342309_search_terms( $query, $taxonomy ) {
    $per_page = absint( $query->get( 'posts_per_page' ) );
    if ( ! $per_page ) {
        $per_page = max( 10, get_option( 'posts_per_page' ) );
    }

    $paged = max( 1, $query->get( 'paged' ) );
    $offset = absint( ( $paged - 1 ) * $per_page );
    $args = [
        'taxonomy'   => $taxonomy,
//      'hide_empty' => '0',
        'search'     => $query->get( 's' ),
        'number'     => $per_page,
        'offset'     => $offset,
    ];

    $query->terms = [];
    $terms = get_terms( $args );
    if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
        $query->terms = $terms;
    }

    $args['offset'] = 0; // always 0
    $args['fields'] = 'count';
    $query->found_terms = get_terms( $args );

    $query->term_count = count( $query->terms );
    $query->terms_per_page = $per_page; // for WP_Query::$max_num_pages
    $query->is_all_terms = ( (int) $per_page === $query->term_count );

    $query->set( 'posts_per_page', max( 1, $per_page - $query->term_count ) );
    $query->set( 'offset', $query->term_count ? 0 :
        max( 0, $offset - $query->found_terms ) );
}

In the filter_search(), add wpse342309_search_terms( $query, 'resort' ); after this line:

$query->set( 'post_type', array( 'hotel', 'post', 'activities', 'page' ) );

In search.php, for the main WordPress query:

<?php if ( have_posts() ) :

    $total = $wp_query->found_posts + $wp_query->found_terms;
    $wp_query->max_num_pages = ceil( $total / $wp_query->terms_per_page );

    echo '<div class="results">';

    // Display terms matching the search query.
    if ( ! empty( $wp_query->terms ) ) {
        global $term; // for use in the template part (below)
        foreach ( $wp_query->terms as $term ) {
            get_template_part( 'template-parts/content', 'search-term' );
        }
    }

    // Display posts matching the search query.
    if ( ! $wp_query->is_all_terms ) {
        while ( have_posts() ) {
            the_post();
            get_template_part( 'template-parts/content', 'search-post' );
        }
    }

    echo '</div><!-- .results -->';

    // Display pagination.
    //the_posts_pagination();
    echo paginate_links( [
        'total'   => $wp_query->max_num_pages,
        'current' => max( 1, get_query_var( 'paged' ) ),
    ] );

else :
    echo '<h1>No Posts Found</h1>';

endif; // end have_posts()
?>

In search.php, for a custom WordPress query:

You should always manually call wpse342309_search_terms().

<?php
$my_query = new WP_Query( [
    's' => 'foo',
    // ... your args here ...
] );

// Manually apply the terms search.
wpse342309_search_terms( $my_query, 'resort' );

if ( $my_query->have_posts() ) :

    $total = $my_query->found_posts + $my_query->found_terms;
    $my_query->max_num_pages = ceil( $total / $my_query->terms_per_page );

    echo '<div class="results">';

    // Display terms matching the search query.
    if ( ! empty( $my_query->terms ) ) {
        global $term; // for use in the template part (below)
        foreach ( $my_query->terms as $term ) {
            get_template_part( 'template-parts/content', 'search-term' );
        }
    }

    // Display posts matching the search query.
    if ( ! $my_query->is_all_terms ) {
        while ( $my_query->have_posts() ) {
            $my_query->the_post();
            get_template_part( 'template-parts/content', 'search-post' );
        }
    }

    echo '</div><!-- .results -->';

    // Display pagination.
    echo paginate_links( [
        'total'   => $my_query->max_num_pages,
        'current' => max( 1, get_query_var( 'paged' ) ),
    ] );

else :
    echo '<h1>No Posts Found</h1>';

endif; // end have_posts()
?>

Template Part 1: template-parts/content-search-term.php

<?php global $term;
$term_link = get_term_link( $term, 'resort' ); ?>

<div class="result">
    <div class="result-inner">
        <h2><a href="https://wordpress.stackexchange.com/questions/342309/<?php echo esc_url( $term_link ); ?>"><?php echo esc_html( $term->name ); ?></a></h2>

        <p class="blog-more"><a href="https://wordpress.stackexchange.com/questions/342309/<?php echo esc_url( $term_link ); ?>" class="button">View Page</a></p>
    </div>
</div>

Template Part 2: template-parts/content-search-post.php

<?php $type = get_post_type(); ?>

<div class="result">
    <div class="result-inner">
        <h2>
            <?php if ( $type ==  "hotel"  ) { ?>
                Hotel:
            <?php } elseif ( $type == "activities" ) { ?>
                Activity:
            <?php } elseif ( $type == "post" ) { ?>
                Blog Post:
            <?php } elseif ( $type == "page" ) { ?>
                Page:
            <?php } else { ?>
                Page:
            <?php } ?>

            <a href="https://wordpress.stackexchange.com/questions/342309/<?php the_permalink(); ?>"><?php the_title(); ?></a>
        </h2>

        <p class="blog-more"><a href="https://wordpress.stackexchange.com/questions/342309/<?php the_permalink(); ?>" class="button">View Page</a></p>
    </div>
</div>

Leave a Comment