Skip to content

Instantly share code, notes, and snippets.

@joshuadavidnelson
Last active February 27, 2023 13:44
Show Gist options
  • Save joshuadavidnelson/4e82bbd506b776534a6f to your computer and use it in GitHub Desktop.
Save joshuadavidnelson/4e82bbd506b776534a6f to your computer and use it in GitHub Desktop.
Filter wp_dropdown_categories by post type.
<?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 );
<?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;
}
<?php
/**
* A wrapper function for the custom dropdown, sets the current value by a url query var,
* adds a custom label, and a wrapper element.
*
* @author Joshua David Nelson, [email protected]
*
* @link https://joshuadnelson.com/category-taxonomy-dropdown-filtered-by-post-type/
*
* @see https://codex.wordpress.org/Function_Reference/wp_dropdown_categories
*
* @param array $args
*
* @return string $output
*/
function jdn_get_custom_taxonomy_dropdown( $args ) {
// Taxonomy dropdown arguments
$defaults = array(
'taxonomy' => false, // required, the default here is a failsafe
'post_type' => false, // pass a value if you want to
'echo' => false, // true to echo, false for return
'label' => false, // false or pass a string for the label
'query_var' => false, // pass the query variable for the selected dropdown value
'id' => '', // set this and the default category 'name' argument
'wrap' => 'div', // the wrapper element. set to false to skip it.
'wrap_class' => 'select-wrap', // The custom wrapper class, set to false to skip it
);
$args = wp_parse_args( $args, $defaults );
// Be sure we're good to go.
if( ! $args['taxonomy'] )
return false;
// Current value of query variable, if there is one
if( $args['query_var'] ) {
$query_tax = get_query_var( esc_attr( $args['query_var'] ), false );
$current = $query_tax ? sanitize_text_field( $query_tax ) : '';
$args['selected'] = $current;
}
// We're returning the output of wp_dropdown_cateogies, but we need to know how the end user wants it back in this function,
// so we'll store that value as a variable and reset the echo argument for the wp_dropdown_categories.
$echo = esc_attr( $args['echo'] );
$args['echo'] = false;
// Grab the category dropdown
$tax_drop = wp_dropdown_categories( $args );
// Add a label
$label = ( $args['label'] && !empty( $args['id'] ) ) ? '<label for="' . esc_attr( $args['id'] ) . '">' . esc_attr( $args['label'] ) . '</label>' : '';
// Wrap it up
$class = $args['wrap_class'] ? ' class="' . esc_attr( $args['wrap_class'] ) . '"' : '';
$tax_drop = ( $args['wrap'] && !empty( $tax_drop ) ) ? '<' . esc_attr( $args['wrap'] ) . $class . '>' . $label . $tax_drop . '</' . esc_attr( $args['wrap'] ) . '>' : $tax_drop;
// Return it or echo it back
if( $echo ) {
echo $tax_drop;
} else {
return $tax_drop;
}
}
@19h47
Copy link

19h47 commented Jul 4, 2019

Very useful, thank you!

I had to query only post with post_status='publish' so I had to the $clauses['where'] this parameter

$clauses['where'] .= ' AND p.post_type IN (' . $post_types_string . ') AND p.post_status=\'publish\'';

Not sure if it’s the best solution but it works!

@lw-na
Copy link

lw-na commented Sep 2, 2020

Great! thank you for sharing.
How to place text "Please Select" at very first element of dropdown list.

@joshuadavidnelson
Copy link
Author

Hey @lw-na you can use the show_option_none parameter from wp_dropdown_categories (see the WP documentation here), as the arguments for the jdn_get_custom_taxonomy_dropdown function get passed to the core function.

Either by adding it to the arguments passed in the custom function when it's called:

$args = array(
	'show_option_none' => 'Please select',
	'taxonomy'         => 'category'
);
jdn_get_custom_taxonomy_dropdown( $args );

OR, if you want this to be the default, you can update the defaults in the function:

// Taxonomy dropdown arguments
$defaults = array(
	'show_option_none' => 'Please Select',
	'taxonomy'         => false, 	// required, the default here is a failsafe
	'post_type'        => false, 	// pass a value if you want to
	'echo'             => false, 	// true to echo, false for return
	'label'            => false, 	// false or pass a string for the label
	'query_var'        => false, 	// pass the query variable for the selected dropdown value
	'id'               => '',        // set this and the default category 'name' argument
	'wrap'             => 'div', 	// the wrapper element. set to false to skip it.
	'wrap_class'       => 'select-wrap', // The custom wrapper class, set to false to skip it
);
$args = wp_parse_args( $args, $defaults );

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