Category (Taxonomy) Dropdown Filtered By Post Type

Screen Shot 2016-02-19 at 9.20.28 PMWhile working on a large custom content management solution for a client I was building templates for multiple post types all sharing a common taxonomy. Most of these templates needed a dropdown to filter the results by the common taxonomy, in this case a taxonomy named “department.”

Should be fairly straight-forward, right? WordPress does offer a great function for creating taxonomy dropdowns called wp_dropdown_categories, which works with custom taxonomies as well as categories.

However, it will return all taxonomies with any posts. If you have multiple post types, you could very easily get a list of taxonomies with posts, but not the post type you want for the template – as was my issue.

Luckily, we can filter the SQL query used in the get_terms function, which is called by wp_dropdown_cateogies.

* Filter the term clauses using the arguments, specifically for the wp_dropdown_categories.
* @see
* @see
* @param array $clauses
* @param string $taxonomy
* @param array $args
* @return array
add_filter( 'terms_clauses', 'jdn_post_type_terms_clauses', 10, 3 );
function jdn_post_type_terms_clauses( $clauses, $taxonomy, $args ) {
// Make sure we have a post_type argument to run with.
if( !isset( $args['post_type'] ) || empty( $args['post_type'] ) )
return $clauses;
global $wpdb;
// Setup the post types in an array
$post_types = array();
// If the argument is an array, check each one and cycle through the post types
if( is_array( $args['post_type'] ) ) {
// All possible, public post types
$possible_post_types = get_post_types( array( 'public' => true ) );
// Cycle through the post types, add them to our array if they are public
foreach( $args['post_type'] as $post_type ) {
if( in_array( $post_type, $possible_post_types ) )
$post_types[] = "'" . esc_attr( $post_type ) . "\'";
// If the post type argument is a string, not an array
} elseif( is_string( $args['post_type'] ) ) {
$post_types[] = "'" . esc_attr( $args['post_type'] ) . "'";
// If we have valid post types, build the new sql
if( !empty( $post_types ) ) {
$post_types_string = implode( ',', $post_types );
$fields = str_replace( 'tt.*', 'tt.term_taxonomy_id, tt.term_id, tt.taxonomy, tt.description, tt.parent', $clauses['fields'] );
$clauses['fields'] = 'DISTINCT ' . esc_sql( $fields ) . ', COUNT(t.term_id) AS count';
$clauses['join'] .= ' INNER JOIN ' . $wpdb->term_relationships . ' AS r ON r.term_taxonomy_id = tt.term_taxonomy_id INNER JOIN ' . $wpdb->posts . ' AS p ON p.ID = r.object_id';
$clauses['where'] .= ' AND p.post_type IN (' . $post_types_string . ')';
$clauses['orderby'] = 'GROUP BY t.term_id ' . $clauses['orderby'];
return $clauses;

The above utilizes the term_clauses filter, and hunts for the post_type argument. The beauty of this is that we’re extending the functionality of the get_terms function directly.

This means we can use wp_dropdown_categories and simply pass our new post_type argument. Furthermore, the fallback here is the built-in function, which is a double bonus. I love the extend-ability of WordPress!

Here’s the basic usage, now that we have the filter above:

* Using wp_dropdown_categories with the post type filter applied.
* @link
// Taxonomy dropdown arguments
$args = array(
'taxonomy' => 'department',
'post_type' => 'project', // filter by the post type 'project'
'echo' => true,
wp_dropdown_categories( $args );

That said, I built a custom function to wrap the wp_dropdown_categories, so I could grab the current query var and add a wrapper element.

Here are some useful resources if you’re going down this rabbit hole:


  1. Ah very cool. I was wondering how to do something like this. Yet another thing to add to my list to try out. Thanks very much.