Keyword search limited to specific post type filtered by multiple custom taxonomies?

Is it possible to limit a search for keywords to a specific post type (say ‘news’) but filter the results by selected custom taxonomies?

  • I have a news section.
  • This has the custom post type called ‘news’.
  • This then has the custom taxonomy ‘news-category’.
  • There are several custom taxonomy terms that need to be listed with
    checkboxes next to them.

Say for instance someone wants to search for the keyword “Wales” and they check the custom taxonomies “Medical” and “Financial”, then it displays the results by those search queries.

To sum up, they are only searching within the news post type which is filtered by the custom taxonomies and keyword. Is this possible? If so, how?


UPDATE:

I have a template called archive-members.php with the following, how can I wrap all this with the necessary code to show a search with keyword+category, just keyword or just category.

<?php
/**
 * @package WordPress
 * @subpackage Default_Theme
 */

get_header();
?>

        <!-- Main Content -->

        <div class="post" id="post-<?php the_ID(); ?>">
            <div class="entry">

                <div class="left-search">
                    <div class="page-title-search">
                        <h2>Search</h2>
                        <form id="custom-search" action="<?php bloginfo('url'); ?>/" method="get">
                        <?php //Get all (non-empty) terms for taxonomy 'news-category'
                        $args = array('orderby' => 'name','order' => 'ASC');
                        $categories = get_terms( 'members', $args );
                        ?>
                        <!-- Visible input for search term -->
                        <p style="margin-bottom:5px!IMPORTANT;"><b>Keyword</b></p>
                        <input type="text" class="keyword" name="s" value="" />
                        <div class="clear"></div>

                        <p style="margin-top:20px!IMPORTANT;margin-bottom:2px!IMPORTANT;"><b>Specialisms</b></p>
                        <?php //Display checkbox for each term
                        $counter = 1;
                        foreach ($categories as $category) {
                            echo '<input type="checkbox" id="field-'.$counter.'" class="mycheckbox" name="mw-filter-terms[]" value="'.$category->slug.'">';
                            echo '<label for="field-'.$counter.'" class="mycheckbox-label">'.esc_html($category->name).'</label>';
                        $counter++; 
                        } ?>

                        <!-- Hidden input to set post type to news-->
                        <input type="hidden" name="post_type" value="members" />

                        <div class="clear"></div>
                        <!-- Submit button -->
                        <button class="blue medium awesome awesomeforward awesomesearch" type="submit">Search</button>
                        </form>                     


                    </div>
                </div>

                <div class="news-content" style="background-color:#ececec!IMPORTANT;">
                    <div class="page-title-content">
                        <h2>Members Directory</h2>
                    </div>
                    <div class="news-content-inner">
                        <div class="inner-holder">
                            <?php the_field('content', 1886); ?>
                        </div>
                    </div>
                            <a class="blue medium awesome awesomeforward" style="margin-left:193px;margin-bottom:10px;color: white !important; " href="<?php bloginfo('url'); ?>/member-signup/">Become A Member</a>                    

                <div class="news-content" style="background-color:#ececec!IMPORTANT;">
                    <div class="page-title-content">
                        <h2>Search Results</h2>
                    </div>
                    <div class="news-content-inner">
                        <?php $portfolioloop1 = new WP_Query( array( 'paged' => get_query_var('paged'), 'order' => 'ASC', 'orderby' => 'title', 'post_status' => 'publish', 'post_type' => 'members', 'posts_per_page' => 300 ) ); ?>
                        <?php while ( $portfolioloop1->have_posts() ) : $portfolioloop1->the_post(); ?>
                        <div <?php if (get_field('logo') != "") { ?>style="height:120px;"<?php } ?> class="news-item" onclick="location.href='<?php echo the_permalink(); ?>'">
                            <?php if (get_field('logo') != "") { ?>
                                <div style="height:110px;float:left;">
                                <table>
                                    <tr>
                                    <td height="110">
                                    <img style="margin-right:15px;" src="<?php echo the_field('logo'); ?>" width="150" alt="<?php echo the_title(); ?>" />
                                    </td>
                                    </tr>
                                </table>
                                </div>
                            <?php } ?>
                            <div <?php if (get_field('logo') != "") { ?>style="float:left;width:379px;"<?php } ?>>
                                <h2><a style="color:#AA3CA7!important;" href="<?php echo the_permalink(); ?>"><?php echo the_title(); ?></a></h2>
                                <p class="news-page">
                                    <?php $description = get_field('description');
                                    echo substr($description,0,300) . "..." ?>
                                </p>
                            </div>  
                        </div>
                        <?php endwhile; // end of the loop. ?>

                    </div>
                <div class="clear"></div>                   
            </div>
        </div>    

<?php get_footer(); ?>

Solutions Collecting From Web of "Keyword search limited to specific post type filtered by multiple custom taxonomies?"

It’s fairly inefficient to use query_posts and I often find it’s more trouble than it’s worth. Instead, to set the post type you can just have a hidden input inside the form with name ‘post_type’ and value (in this example) ‘news’. WordPress handles the rest.

Unfortunately it is not so easy with taxonomies (maybe I’ve missed a trick?). However, rather than redo-ing the search with query_posts, you can just use the pre_get_posts filter which runs before the database is queried to filter by taxonomy term.

The following assumes your news category has name ‘news-category’ and your post type is ‘news’:

(First remember to remove your query_posts stuff from the search template – as this just over-rides the default query WordPress performs).

HTML markup

This is the form that displays your search:

<form action="" method="get">
<?php
 //Get all (non-empty) terms for taxonomy 'news-category'
 $args = array('orderby' => 'name','order' => 'ASC');
 $categories = get_terms( 'news-category', $args );

 //Display checkbox for each term
 foreach ($categories as $category) {
       echo '<input type="checkbox" name="my-filter-terms[]" value="'.$category->slug.'">'.esc_html($category->name);
  }
  ?>

    <!-- Hidden input to set post type to news-->
    <input type="hidden" name="post_type" value="news" />

    <!-- Visible input for search term -->
    <input type="text" name="s" value="" />

    <!-- Submit button -->
    <input type="submit" />
 </form>

I’ve used my-filter-terms to store the array of slugs of the checked terms. (It would probably be better to register a custom variable, but I’ll leave that for now. I had hoped to be able to use the default taxonomy query variable but it didn’t seem to work).

Filter the Search

The post type and search term will be automatically handled. The following is to filter by taxonomy term. This goes in your theme’s functions.php:

add_filter('pre_get_posts','my_filter_the_search',10,1);
function my_filter_the_search($query){

    //If the query is a search AND taxonomy terms are set, filter by those terms:
    if($query->is_search() && isset($_GET['my-filter-terms'])){
        //Get array of slugs of checked terms
        $terms = (array) $_GET['my-filter-terms'];

        //Tax_query array
        $tax_query = array(array(
                    'taxonomy' => 'news-category',
                    'field' => 'slug',
                    'terms' => $terms,
                    'operator' => 'IN',
                )); 

        //Tell the query to filter by tax
        $query->set('tax_query', $tax_query  );
    }
    return $query;
}

The operator is set to IN. This means it searches any posts in any one of the checked terms. You could set it to AND to search posts which are in all the checked terms.

You should be able to do this by combining the ‘tax_query’ and ‘s’ parameters of a WP_Query. I’m assuming here that you are using URL parameters to accomplish this, but you can set up the initial variables however you want.

$search_terms = isset( $_GET['s'] ) ? urldecode( $_GET['s'] ) : '';
$news_category = isset( $_GET['news_category'] ) ? urldecode( $_GET['news_category'] ) : '';


$query_args = array( 'post_type' => 'news', 'tax_query' => array() );

if ( !empty( $search_terms ) ) {
    $query_args['s'] = $search_terms;
}

if ( !empty( $search_terms ) ) {
    $query_args['tax_query'][] = array(
        array(
            'taxonomy' => 'news',
            'field' => 'slug',
            'terms' => $news_category
        )
    );
}

$my_posts = new WP_Query( $query_args );
if ( $my_posts->have_posts() ) { // etc

More on WP_Query parameters, from the wordpress.org codex: https://codex.wordpress.org/Class_Reference/WP_Query