Skip to content

Instantly share code, notes, and snippets.

@mikeschinkel
Created August 21, 2010 00:21
Show Gist options
  • Save mikeschinkel/541505 to your computer and use it in GitHub Desktop.
Save mikeschinkel/541505 to your computer and use it in GitHub Desktop.
<?php
/*
Description: Adds a taxonomy filter in the admin list page for a custom post type.
Written for: http://wordpress.stackexchange.com/posts/582/
By: Mike Schinkel - http://mikeschinkel.com/custom-workpress-plugins
Instructions: Put this code in your theme's functions.php file or inside your own plugin. Edit to suite your post types and taxonomies. Hope this helps...
*/
add_filter('manage_listing_posts_columns', 'add_businesses_column_to_listing_list');
function add_businesses_column_to_listing_list( $posts_columns ) {
if (!isset($posts_columns['author'])) {
$new_posts_columns = $posts_columns;
} else {
$new_posts_columns = array();
$index = 0;
foreach($posts_columns as $key => $posts_column) {
if ($key=='author') {
$new_posts_columns['businesses'] = null;
}
$new_posts_columns[$key] = $posts_column;
}
}
$new_posts_columns['businesses'] = 'Businesses';
return $new_posts_columns;
}
add_action('manage_posts_custom_column', 'show_businesses_column_for_listing_list',10,2);
function show_businesses_column_for_listing_list( $column_id,$post_id ) {
global $typenow;
if ($typenow=='listing') {
$taxonomy = 'business';
switch ($column_id) {
case 'businesses':
$businesses = get_the_terms($post_id,$taxonomy);
if (is_array($businesses)) {
foreach($businesses as $key => $business) {
$edit_link = get_term_link($business,$taxonomy);
$businesses[$key] = '<a href="'.$edit_link.'">' . $business->name . '</a>';
}
echo implode(' | ',$businesses);
}
break;
}
}
}
/* JUST AN HYPOTHETICAL EXAMPLE
add_action('manage_posts_custom_column', 'manage_posts_custom_column',10,2);
function manage_posts_custom_column( $column_id,$post_id ) {
global $typenow;
switch ("{$typenow}:{$column_id}") {
case 'listing:business':
echo '...whatever...';
break;
case 'listing:property':
echo '...whatever...';
break;
case 'agent:listing':
echo '...whatever...';
break;
}
}
*/
add_action('restrict_manage_posts','restrict_listings_by_business');
function restrict_listings_by_business() {
global $typenow;
global $wp_query;
if ($typenow=='listing') {
$taxonomy = 'business';
$business_taxonomy = get_taxonomy($taxonomy);
wp_dropdown_categories(array(
'show_option_all' => __("Show All {$business_taxonomy->label}"),
'taxonomy' => $taxonomy,
'name' => 'business',
'orderby' => 'name',
'selected' => $wp_query->query['term'],
'hierarchical' => true,
'depth' => 3,
'show_count' => true, // This will give a view
'hide_empty' => true, // This will give false positives, i.e. one's not empty related to the other terms. TODO: Fix that
));
}
}
add_filter('parse_query','convert_business_id_to_taxonomy_term_in_query');
function convert_business_id_to_taxonomy_term_in_query($query) {
global $pagenow;
$qv = &$query->query_vars;
if ($pagenow=='edit.php' &&
isset($qv['taxonomy']) && $qv['taxonomy']=='business' &&
isset($qv['term']) && is_numeric($qv['term'])) {
$term = get_term_by('id',$qv['term'],'business');
$qv['term'] = $term->slug;
}
}
add_action('init','register_listing_post_type');
function register_listing_post_type() {
register_post_type('listing',array(
'label' => 'Listings',
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'query_var' => true,
'rewrite' => true,
'capability_type' => 'post',
'hierarchical' => false,
));
}
add_action('init','register_business_taxonomy');
function register_business_taxonomy() {
register_taxonomy('business',array('listing'),array(
'label' => 'Businesses',
'public'=>true,
'hierarchical'=>true,
'show_ui'=>true,
'query_var'=>true
));
}
@jaredwilli
Copy link

This could be useful if/when adding or creating a similar class to the post types one I'm making now, but for taxonomies. I don't know if it would be a good idea to combine the two, better to keep them separate I think to keep the code more clean and organized.

I like this tho, nice.

@kreviii
Copy link

kreviii commented Dec 12, 2010

First of all thank you, this rocks fox socks off.

I think that this first 'add_action' may need to be 'add_filter' , at least that seemed to make a difference for me,
as well as the naming of the hook seems as if it need to match with the post type, Im I correct? - not sure but basically I had some troubles getting it going at first with this code
so ...
add_action('manage_listing_posts_columns', 'add_businesses_column_to_listing_list');
should be ?
add_filter('manage_<* put your post type here *>_posts_columns', 'add_businesses_column_to_listing_list');

@jaredwilli
Copy link

Yeah, that's what you have to do. 'manage_{posttype}_posts_columns'.

I also use add_filter for the manage post custom columns hooks too myself. Never tried to use add action before, never had any problem with add filter

@mikeschinkel
Copy link
Author

@jaredwilli - Sorry for the late reply. Client requirements are consuming all available time. Not sure what exactly you are looking to do..

@ kreviii - Thanks for the kudos. You are right about the the action vs. filter OTOH it really ends up being nothing more than code documentation because one just calls the other so either work. Still, I'll change it.

@jaredwilli
Copy link

@mike Well I actually have already developed 2 classes yesterday. One for post types and one for taxonomies. They enable you to create them with the use of a single line of code. I realized that this may not be worth including in the class as I first thought, but it's still a great snippet.

@chodorowicz
Copy link

And what's up with this code:
add_action('manage_posts_custom_column', 'show_businesses_column_for_listing_list',10,2);
What's the the 10 and 2? Do I have to replace it with some other IDs? I cannot get this part to work

@mikeschinkel
Copy link
Author

@chodorowicz - 10 is priority (the default) and 2 is the number of parameters that get passed to the function that hooks 'manage_posts_custom_column', in this case $column_id and $post_id. No IDs needed here.

@chodorowicz
Copy link

@mikeschinkel thanks. Finally the problem was that my custom post type was hierarchical so I had to use manage_pages_custom_column instead of manage_posts_custom_column. Maybe this helps someone.

@mroncetwice
Copy link

hello, everyone, total n00b here-

I've tried to apply Mike's code and it seems like everything works EXCEPT for the last function where the taxonomy term ID is supposed to be converted to the corresponding slug .. Basically, when I apply one of the taxonomy filters in the admin area of my custom post type, I get a result of "no [cpt term]s found" and the resulting URL shows the taxonomy term ID instead the slug. However if I manually replace said ID with the appropriate slug, I do indeed see the intended results and that is why I think my problem lies in the failure of the conversion function..

SO, I'm hoping someone can take a look at this code below and tell me what I might be doing wrong or am missing or whatever. Thanks in advance:

add_filter('manage_our_properties_posts_columns', 'add_property_types_column_to_our_properties_list');
function add_property_types_column_to_our_properties_list( $posts_columns ) {
    if (!isset($posts_columns['author'])) {
        $new_posts_columns = $posts_columns;
    } else {
        $new_posts_columns = array();
        $index = 0;
        foreach($posts_columns as $key => $posts_column) {
            if ($key=='author') {
            $new_posts_columns['property-types'] = null;
            }
            $new_posts_columns[$key] = $posts_column;
        }
    }
    $new_posts_columns['property-types'] = 'Property Types';
    return $new_posts_columns;
}

add_action('manage_posts_custom_column', 'show_property_types_column_for_our_properties_list',10,2);
function show_property_types_column_for_our_properties_list( $column_id,$post_id ) {
    global $typenow;
    if ($typenow=='our_properties') {
        $taxonomy = 'property-types';
        switch ($column_id) {
        case 'property-types':
            $property_types = get_the_terms($post_id,$taxonomy);
            if (is_array($property_types)) {
                foreach($property_types as $key => $property_type) {
                    $edit_link = get_term_link($property_type,$taxonomy);
                    $property_types[$key] = '<a href="'.$edit_link.'">' . $property_type->name . '</a>';
                }
                echo implode(' | ',$property_types);
            }
            break;
        }
    }
}

add_action('restrict_manage_posts','restrict_our_properties_by_property_types');
function restrict_our_properties_by_property_types() {
    global $typenow;
    global $wp_query;
    if ($typenow=='our_properties') {
        $taxonomy = 'property-types';
        $property_types_taxonomy = get_taxonomy($taxonomy);
        wp_dropdown_categories(array(
            'show_option_all' =>  __("Show All {$property_types_taxonomy->label}"),
            'taxonomy'        =>  $taxonomy,
            'name'            =>  'property-types',
            'orderby'         =>  'name',
            'selected'        =>  $wp_query->query['term'],
            'hierarchical'    =>  false,
            'depth'           =>  3,
            'show_count'      =>  true,  // This will give a view
            'hide_empty'      =>  true,   // This will give false positives, i.e. one's not empty related to the other terms. TODO: Fix that
        ));
    }
}
add_filter('parse_query','convert_property_types_id_to_taxonomy_term_in_query');
function convert_property_types_id_to_taxonomy_term_in_query($query) {
    global $typenow;
    global $pagenow;
    global $wp_query;
    $qv = &$query->query_vars;
    if ($pagenow=='edit.php' && isset($qv['taxonomy']) && $qv['taxonomy']=='property-types' && isset($qv['term']) && is_numeric($qv['term'])) {
        $term = get_term_by('id',$qv['term'],'property-types');
        $qv['term'] = $term->slug;
    }
}




//// now do actual registration




// The register_post_type() function is not to be used before the 'init'.
add_action( 'init', 'our_properties_init' );

/* Here's how to create your customized labels */
function our_properties_init() {
    $labels = array(
        'name' => _x( 'Our Properties', 'post type general name' ), // Tip: _x('') is used for localization
        'singular_name' => _x( 'Property', 'post type singular name' ),
        'add_new' => _x( 'Add New', 'property' ),
        'add_new_item' => __( 'Add New Property' ),
        'edit_item' => __( 'Edit Property' ),
        'new_item' => __( 'New Property' ),
        'view_item' => __( 'View Property' ),
        'search_items' => __( 'Search Our Properties' ),
        'not_found' =>  __( 'No Properties found' ),
        'not_found_in_trash' => __( 'No properties found in Trash' ),
        'parent_item_colon' => ''
    );

    // Create an array for the $args
    $args = array( 'labels' => $labels, /* NOTICE: the $labels variable is used here... */
        'public' => true,
        'publicly_queryable' => true,
        'show_ui' => true,
        'query_var' => true,
        'rewrite' => true,
        'capability_type' => 'post',
        'hierarchical' => false,
        'menu_position' => null,
        'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' )
    ); 

    register_post_type( 'our_properties', $args ); /* Register it and move on */
}

// hook into the init action and call create_property_taxonomies() when it fires
add_action( 'init', 'create_property_taxonomies', 0 );

// create two taxonomies, genres and writers for the post type "book"
function create_property_taxonomies() {

    // Add new taxonomy, make it hierarchical (like categories)
    $labels = array(
        'name' => _x( 'Property Types', 'taxonomy general name' ),
        'singular_name' => _x( 'Property Type', 'taxonomy singular name' ),
        'search_items' =>  __( 'Search Property Types' ),
        'all_items' => __( 'All Property Types' ),
        'parent_item' => __( 'Parent Property Type' ),
        'parent_item_colon' => __( 'Parent Property Type:' ),
        'edit_item' => __( 'Edit Property Type' ),
        'update_item' => __( 'Update Property Type' ),
        'add_new_item' => __( 'Add New Property Type' ),
        'new_item_name' => __( 'New Property Type Name' ),
    );  

    register_taxonomy( 'property-types', array( 'our_properties' ), array(
        'label' => 'Property Types',
        'hierarchical' => true,
        'labels' => $labels, /* NOTICE: Here is where the $labels variable is used */
        'show_ui' => true,
        'query_var' => true,
        'rewrite' => array( 'slug' => 'property-types' )
    ));
}

@chodorowicz
Copy link

it's much easier now in WP 3.1

function my_restrict_manage_posts() {
    global $typenow;
    $args=array( 'public' => true, '_builtin' => false ); 
    $post_types = get_post_types($args);
    if ( in_array($typenow, $post_types) ) {
    $filters = get_object_taxonomies($typenow);
        foreach ($filters as $tax_slug) {
            $tax_obj = get_taxonomy($tax_slug);
            wp_dropdown_categories(array(
                'show_option_all' => __('Show All '.$tax_obj->label ),
                'taxonomy' => $tax_slug,
                'name' => $tax_obj->name,
                'orderby' => 'term_order',
                'selected' => $_GET[$tax_obj->query_var],
                'hierarchical' => $tax_obj->hierarchical,
                'show_count' => false,
                'hide_empty' => true
            ));
        }
    }
}
function my_convert_restrict($query) {
    global $pagenow;
    global $typenow;
    if ($pagenow=='edit.php') {
        $filters = get_object_taxonomies($typenow);
        foreach ($filters as $tax_slug) {
            $var = &$query->query_vars[$tax_slug];
            if ( isset($var) ) {
                $term = get_term_by('id',$var,$tax_slug);
                $var = $term->slug;
            }
        }
    }
}
add_action('restrict_manage_posts', 'my_restrict_manage_posts' );
add_filter('parse_query','my_convert_restrict');

@mroncetwice
Copy link

I tried the above code before writing here and again just now after your reply (thank you for such a quick reply btw), but that solution gives me a drop-down menu with nothing in it (and therefore is unclickable.. or is at least unresponsive) and also does not show me the custom taxonomy column list .. It isn't apparent to me, but is there maybe somewhere I need to plug in my custom post type or custom taxonomy or something else that needs to be customized to work with my specific setup?

@chodorowicz
Copy link

As to the first problem, I recalled that I had the same problem (empty dropdown). Commenting out (or removing) following part helped me out:
// 'orderby' => 'term_order',

As to the column list then you'd need to look for this part of code somewhere else or try to extract needed parts from mikeschinkel's code - I haven't needed it yet and I'm not prof. in WP enough to write this kind of code out of my head ;). Greets!

@mroncetwice
Copy link

YES!

:D

@monab
Copy link

monab commented Jul 23, 2013

Can anybody please let me know how to use this code? cause I am new be in wordpress. and want to do add filter to custom category.

@qant
Copy link

qant commented Nov 7, 2017

Is this still ok to use this code for current 4.8 Wordpress version?

@Armaniimus
Copy link

Thanks chodorowich for your code example in the comments. I noticed that it only filters the first taxonomy if 2 are selected do you have suggestions on how I could fix this?

@david-kirkland
Copy link

You should make this into a plugin.

@visualcreature
Copy link

it's much easier now in WP 3.1

function my_restrict_manage_posts() {
    global $typenow;
    $args=array( 'public' => true, '_builtin' => false ); 
    $post_types = get_post_types($args);
    if ( in_array($typenow, $post_types) ) {
    $filters = get_object_taxonomies($typenow);
        foreach ($filters as $tax_slug) {
            $tax_obj = get_taxonomy($tax_slug);
            wp_dropdown_categories(array(
                'show_option_all' => __('Show All '.$tax_obj->label ),
                'taxonomy' => $tax_slug,
                'name' => $tax_obj->name,
                'orderby' => 'term_order',
                'selected' => $_GET[$tax_obj->query_var],
                'hierarchical' => $tax_obj->hierarchical,
                'show_count' => false,
                'hide_empty' => true
            ));
        }
    }
}
function my_convert_restrict($query) {
    global $pagenow;
    global $typenow;
    if ($pagenow=='edit.php') {
        $filters = get_object_taxonomies($typenow);
        foreach ($filters as $tax_slug) {
            $var = &$query->query_vars[$tax_slug];
            if ( isset($var) ) {
                $term = get_term_by('id',$var,$tax_slug);
                $var = $term->slug;
            }
        }
    }
}
add_action('restrict_manage_posts', 'my_restrict_manage_posts' );
add_filter('parse_query','my_convert_restrict');

This code worked perfectly for me in 2020 using WP 5.4! My situation is as follows:
I've created a custom post type Jobs and added two taxonomies (Branche and Status). I needed a filter to show in the admin to quickly filter the status (open/closed) within a branche. Happy to got it working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment