» » » Filter Posts by Custom Taxonomy in Admin area in WordPress

Filter Posts by Custom Taxonomy in Admin area in WordPress

posted in: WordPress
3 min read

Let’s say we have a custom post type called “hotels“. We create the custom field named “country” and “rating” and add both fields into the “hotels” post type. In the Hotels page at the admin area, it shows hotel title, country, rating, author and date columns as the screenshot below.

figure: hotels page

There are two hooks that involve in this solution. One is ” restrict_manage_posts” and another one is “parse_query “. The restrict_manage_posts hook will add the dropdown filter at the admin page. While the parse_query hook will add the selected filter into the query.

Add country field as a filter

A country field is a custom field of the hotels post type. The country field is created as a post type. First, we will add a country filter. To do that, in the functions.php, we will add the code below.

Note that, if the current theme is not the child theme, I strongly recommend you to create the child theme and create the functions.php in there. Otherwise, when the current theme is updated, the custom code we add will be overridden.

/* Add country filter */
function my_hotel_country_filter_list() {
    $screen = get_current_screen();
    global $wp_query;
    if ($screen->post_type == 'hotels') {

        // get all countries
        $query = new WP_Query(array(
                 'post_type' => 'country', 
                 'post_status' => 'publish', 
                 'posts_per_page' => -1, 
                 'orderby' => array('title' => 'ASC')
                 ));
        $countries = $query->get_posts();

        // current user data
        $current_user = wp_get_current_user();
        $_countries = get_user_meta($current_user->ID, $key = '_user_countries', true);
        ?>
        <select name="country_filter">
            <option value="">All countries</option>
            <?php foreach ($countries as $obj): $int_code = get_post_meta($obj->ID, 'country_code', true); ?>
                <?php
               if (!current_user_can('manage_options')&& !in_array($int_code, $_countries) )
                   continue;
                ?>
                <option value="<?php echo $int_code ?>" <?php echo ( isset($_GET['country_filter']) ? ($_GET['country_filter'] == $int_code ? 'selected' : '') : '' ) ?>><?php echo $obj->post_title ?></option>
            <?php endforeach; ?>
        </select>
        <?php
    }
}
add_action('restrict_manage_posts', 'my_hotel_country_filter_list');

Now refresh the hotels page at the admin area, you will see the country filter. Next, we will add the selected country filter into the query condition. So that we will get the query result according to the selected filter. To do that, we will add the code below.

function perform_hotels_country_filtering($query) {
    global $pagenow;
    $type = 'post';
    if (isset($_GET['post_type'])) {
        $type = $_GET['post_type'];
    }
    if ('hotels' == $type && is_admin() && $pagenow == 'edit.php' && isset($_GET['country_filter']) && $_GET['country_filter'] != '' && $query->is_main_query()) {

        $query->query_vars['meta_query'][] = array('key' => 'hotel_country', 'value' => $_GET['country_filter']);
    }
}
add_filter('parse_query', 'perform_hotels_country_filtering');

Now refresh the hotels page at the admin area, you select the country from the country filter then hits the “Filter” button. It should show the result filtered by the selected country filter. If not, you have to check the query string and make sure the query_vars is set correctly.

Add rating field as a filter

Similar to adding the country filter, we will add the rating filter by using the code below. Again, we will add the code into the functions.php. The rating is created as a taxonomy.

// # add rating filter
function my_hotel_rating_filter_list() {
    $screen = get_current_screen();
    global $wp_query;
    if ($screen->post_type == 'hotels') {

        // get all hotel ratings
        $terms = get_terms('hotel_ratings', array(
            'orderby' => 'name',
            'hide_empty' => 0
        ));

        // current user data
        $current_user = wp_get_current_user();
        $_countries = get_user_meta($current_user->ID, $key = '_user_countries', true);
        ?>
        <select name="rating_filter">
            <option value="">All ratings</option>
            <?php
            foreach ($terms as $term) {
                ?>
                <option value="<?php echo $term->slug ?>" <?php echo ( isset($_GET['rating_filter']) ? ($_GET['rating_filter'] == $term->slug ? 'selected' : '') : '' ) ?>><?php echo $term->name ?></option>
                <?php
            }
            ?>
        </select>
        <?php
    }
}
add_action('restrict_manage_posts', 'my_hotel_rating_filter_list');

Now refresh the hotels page at the admin area, you should see the ratings filter. Next, we will add the selected rating into the query conditions. To do that, we will use the code below.

// # add taxonomy into query conditions
function perform_hotels_rating_filtering($query) {
    global $pagenow;
    $type = 'post';

    if (isset($_GET['post_type'])) {
        $type = $_GET['post_type'];
    }

    if ('hotels' == $type && is_admin() && $pagenow == 'edit.php' && isset($_GET['rating_filter']) && $_GET['rating_filter'] != '' && $query->is_main_query()) {

        $query->query_vars['tax_query'][] = array(
            'taxonomy' => 'hotel_ratings', 
            'field' => 'slug',
            'terms' => $_GET['rating_filter']
        );      
        // this tax_query setting will give you the same result from the count link of rating taxonomy page
    }
}
add_filter('parse_query', 'perform_hotels_rating_filtering');

Refresh the hotels page and tested by using the rating filter, you should see the query result filtered by the selected rating. If not, you have to check the query string and make sure the query_vars is set correctly.

That’s it. It is simple and the filters are useful for the users.