Posted on

Category filter for search results pages

Sometimes it’s nice to have a category filter on the search results pages: a simple dropdown where you can choose the category you want to show. It’s easy to create one using wp_dropdown_categories(), but on a search results page that’s slightly problematic, as it’ll include all categories, not just those included in the search results.

So, here’s a two-part function that fixes that. First, we need a filter that processes the search results and gets a list of categories in the results. This filter runs very late on the relevanssi_hits_filter hook, so that it runs after other filters on the same hook:

add_filter('relevanssi_hits_filter', 'rlv_gather_categories', 99);
function rlv_gather_categories($hits) {
    global $rlv_categories_present;
    $rlv_categories_present = array();
    foreach ( $hits[0] as $hit ) {
        $terms = get_the_terms( $hit->ID, 'category' );
        if (is_array($terms)) {
            foreach ( $terms as $term ) {
                $rlv_categories_present[ $term->term_id ] = $term->name;
            }
        }
    }
    asort( $rlv_categories_present );
    return $hits;
}

If you want this to use WooCommerce product categories, replace the category in the get_the_terms() call with product_cat, like this:

$terms = get_the_terms( $hit->ID, 'product_cat' );

Now that’s in place (put both of these functions to theme functions.php, or some other suitable place), we can create the function that displays the dropdown. It’s a simple dropdown that lists the categories in alphabetical order (that comes from the asort() in the first function, actually) and includes a bit of JavaScript that adds a cat parameter with the correct value to the current URL and reloads the page.

If a category parameter is already in place, there’s instead a link that removes all category filters.

function rlv_category_dropdown() {
    global $rlv_categories_present, $wp_query;
 
    if (!empty($wp_query->query_vars['cat'])) {
        $url = esc_url(remove_query_arg('cat'));
        echo "<p><a href='$url'>Remove category filter</a>.</p>";
    }
    else {
        $select = "<select id='rlv_cat' name='rlv_cat'><option value=''>Choose a category</option>";
        foreach ( $rlv_categories_present as $cat_id => $cat_name ) {
            $select .= "<option value='$cat_id'>$cat_name</option>";
        }
        $select .= "</select>";
        $url = esc_url(remove_query_arg('paged'));
        if (strpos($url, 'page') !== false) {
            $url = preg_replace('/page\/\d+\//', '', $url);
        }
        $select .= <<<EOH
 
<script>
<!--
    var dropdown = document.getElementById("rlv_cat");
    function onCatChange() {
        if ( dropdown.options[dropdown.selectedIndex].value > 0 ) {
            location.href = "$url"+"&cat="+dropdown.options[dropdown.selectedIndex].value;
        }
    }
    dropdown.onchange = onCatChange;
-->
</script>
EOH;
 
        echo $select;
    }
}

For product categories, cat needs to be replaced with product_cat:

function rlv_category_dropdown() {
    global $rlv_categories_present, $wp_query;
 
    if (!empty($wp_query->query_vars['product_cat'])) {
        $url = esc_url(remove_query_arg('product_cat'));
        echo "<p><a href='$url'>Remove category filter</a>.</p>";
    }
    else {
        $select = "<select id='rlv_cat' name='rlv_cat'><option value=''>Choose a category</option>";
        foreach ( $rlv_categories_present as $cat_id => $cat_name ) {
            $select .= "<option value='$cat_id'>$cat_name</option>";
        }
        $select .= "</select>";
        $url = esc_url(remove_query_arg('paged'));
        if (strpos($url, 'page') !== false) {
            $url = preg_replace('/page\/\d+\//', '', $url);
        }
        $select .= <<<EOH
 
<script>
<!--
    var dropdown = document.getElementById("rlv_cat");
    function onCatChange() {
        if ( dropdown.options[dropdown.selectedIndex].value > 0 ) {
            location.href = "$url"+"&product_cat="+dropdown.options[dropdown.selectedIndex].value;
        }
    }
    dropdown.onchange = onCatChange;
-->
</script>
EOH;
 
        echo $select;
    }
}

Once both functions are included in your theme, you can just add a

<?php rlv_category_dropdown(); ?>

to your theme search results template wherever you think is suitable.

One comment Category filter for search results pages

  1. The ability to filter out just posts from a certain category really helps the user to find what they need with just one extra click. This makes an already excellent plugin even more awesome! Thanks so much Mikko!

Leave a Reply

Your email address will not be published. Required fields are marked *