1

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(); ?>
1

2 Answers 2

3
+50

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.

10
  • Thanks very much for the comprehensive answer, really appreciate it. It looks as though it's working as intended, the only thing I need to do is output the results - as it goes to a page not found at the moment. mediwales.com/v3/news How do I output the correct results? What template does it use?
    – Rob
    Mar 19, 2012 at 9:17
  • 1
    Hi Rob, this is because your are posting the search to /v3/news rather than /v3 (which I assume is where WordPress is installed...? Either way, testing with something like .../v3?post_type=news&s=test seems to correctly display the results. You may read this codex page on search pages. In particular the template search.php (if present is used to display the results). Mar 19, 2012 at 12:28
  • 1
    I also recommend using get_search_form(). In which case put the above HTML mark-up inside a template file called searchform.php Mar 19, 2012 at 12:29
  • Aha! Thanks for the help, I just added <?php bloginfo('url'); ?>/ to the action of the form and that directs it to the right page. So it's just a case of repeating this for the events and opportunities and I'm done. I'll leave the bounty for now just in case I've missed anything but you've answered everything so thanks once again. (you'll 100% be getting the bounty!!)
    – Rob
    Mar 19, 2012 at 14:08
  • 1
    Hi Rob - that's because if there is no search query set then WordPress doesn't think its a search. You could remove the $query->is_search(). Then a 'blank search' with 'business support' selected should show you the 'business support' (taxonomy-term) archive page. Apr 30, 2012 at 10:08
1

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

10
  • 1
    Whoops - that second ? should be a :. I've edited the original accordingly. Mar 13, 2012 at 15:03
  • 1
    Also, if you have not used WP_Query before, you might want to read a brief introduction to its use - weblogtoolscollection.com/archives/2008/04/13/… for instance. And I'm assuming that you have a method for creating the links in question; they would have the form of example.com/news/?s=searchterm&news_category=medical Mar 13, 2012 at 15:05
  • I think I can get my head around the WP_Query part, that's just to display the necessary posts right? It's the creating the links I think I need more help on.
    – Rob
    Mar 13, 2012 at 15:10
  • 1
    It's kind of a big job, but I can point you in the right direction. Your category links should be created something like this: add_query_arg( 'news_category', $category_slug, $base_url );, where the $base_url is whatever the underlying structure for the post archives is. Mar 13, 2012 at 15:14
  • 1
    You may find it easier to use codex.wordpress.org/Template_Tags/wp_list_categories, passing 'news_category' as the 'taxonomy' parameter. Actually, that might "just work" without any further customization. Mar 13, 2012 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.