While 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
.
<?php | |
/** | |
* Filter the term clauses using the arguments, specifically for the wp_dropdown_categories. | |
* | |
* @see http://wordpress.stackexchange.com/questions/207655/restrict-taxonomy-dropdown-to-post-type | |
* @see https://www.dfactory.eu/get_terms-post-type/ | |
* | |
* @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:
<?php | |
/** | |
* Using wp_dropdown_categories with the post type filter applied. | |
* | |
* @link https://joshuadnelson.com/category-taxonomy-dropdown-filtered-by-post-type/ | |
*/ | |
// 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:
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.