Question

When you create a custom taxonomy, seemingly the only options are to display it as a tag (non-hierarchical) or category (hierarchical). Is there a way to display them as a dropdown menu?

I realise that this an be done using add_meta_box() and actually manually adding it, but I'm looking for a builtin way (if one exists), to save lots of code! Thanks.

Was it helpful?

Solution

Unfortunately there is no way of doing this with wp_register_taxonomy(). When you register a taxonomy, the default metabox is either checkboxes (hierarchical) or as a cloud (non-hierarchical).

The only way round it is to de-register the existing metabox and add a new-one in its place. By carefully mimicking the 'default' metabox - you won't have to process the data yourself, but let WordPress automatically handle adding / removing terms.

The details of how to do this are given in this answer. You may also find this GitHub repository helpful (which now includes the ability of creating terms on the fly in the new metabox). The linked resources refer to creating a metabox with radio boxes, but it could be easily adapted for drop-down menus.

OTHER TIPS

You can add new taxonomy with your own metabox since wp 3.8 by meta_box_cb

"drop_cat()" - consist of rebuild original core category metabox wp-admin/includes/meta-boxes.php

function realty_type() {
$args = array(
    'show_ui'                    => true,
    'meta_box_cb'                => 'drop_cat',
);
register_taxonomy( 'realty_type', array( 'YOUR_POST_TYPE' ), $args );

}

// Hook into the 'init' action
add_action( 'init', 'realty_type', 0 );


function drop_cat( $post, $box ) {
$defaults = array('taxonomy' => 'category');
if ( !isset($box['args']) || !is_array($box['args']) )
    $args = array();
else
    $args = $box['args'];
extract( wp_parse_args($args, $defaults), EXTR_SKIP );
$tax = get_taxonomy($taxonomy);
?>
<div id="taxonomy-<?php echo $taxonomy; ?>" class="acf-taxonomy-field categorydiv">

        <?php 
        $name = ( $taxonomy == 'category' ) ? 'post_category' : 'tax_input[' . $taxonomy . ']';
        echo "<input type='hidden' name='{$name}[]' value='0' />"; // Allows for an empty term set to be sent. 0 is an invalid Term ID and will be ignored by empty() checks.
        ?>
        <? $term_obj = wp_get_object_terms($post->ID, $taxonomy ); //_log($term_obj[0]->term_id)?>
        <ul id="<?php echo $taxonomy; ?>checklist" data-wp-lists="list:<?php echo $taxonomy?>" class="categorychecklist form-no-clear">
            <?php //wp_terms_checklist($post->ID, array( 'taxonomy' => $taxonomy) ) ?>
        </ul>

        <?php wp_dropdown_categories( array( 'taxonomy' => $taxonomy, 'hide_empty' => 0, 'name' => "{$name}[]", 'selected' => $term_obj[0]->term_id, 'orderby' => 'name', 'hierarchical' => 0, 'show_option_none' => '&mdash;' ) ); ?>

</div>
<?php
}

Here you go:

<select>
    <?php
       $tax_terms = get_terms('your_taxonomy_name', array('hide_empty' => '0'));      
       foreach ( $tax_terms as $tax_term ):  
          echo '<option value="'.$tax_term->name.'">'.$tax_term->name.'</option>';   
       endforeach;
    ?>
</select> 

Delete $tax_terms second argument if you don't want to display empty terms.

Also use selected if you want want to save picked option :)

I was having difficulty with the code provided by Alexufo, so I rewrote it based on the updated function in WP 4.1.2, Here's my updated drop_cat function:

//function below re-purposed from wp-admin/includes/meta-boxes.php - post_categories_meta_box()
function drop_cat( $post, $box ) {
    $defaults = array( 'taxonomy' => 'category' );
    if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) {
        $args = array();
    } else {
        $args = $box['args'];
    }
    $r = wp_parse_args( $args, $defaults );
    $tax_name = esc_attr( $r['taxonomy'] );
    $taxonomy = get_taxonomy( $r['taxonomy'] );
    ?>
    <div id="taxonomy-<?php echo $tax_name; ?>" class="categorydiv">

    <?php //took out tabs for most recent here ?>

        <div id="<?php echo $tax_name; ?>-all">
            <?php
            $name = ( $tax_name == 'category' ) ? 'post_category' : 'tax_input[' . $tax_name . ']';
            echo "<input type='hidden' name='{$name}[]' value='0' />"; // Allows for an empty term set to be sent. 0 is an invalid Term ID and will be ignored by empty() checks.
            ?>
            <ul id="<?php echo $tax_name; ?>checklist" data-wp-lists="list:<?php echo $tax_name; ?>" class="categorychecklist form-no-clear">
                <?php //wp_terms_checklist( $post->ID, array( 'taxonomy' => $tax_name, 'popular_cats' => $popular_ids ) ); ?>
            </ul>

            <?php $term_obj = wp_get_object_terms($post->ID, $tax_name ); //_log($term_obj[0]->term_id) ?>
            <?php wp_dropdown_categories( array( 'taxonomy' => $tax_name, 'hide_empty' => 0, 'name' => "{$name}[]", 'selected' => $term_obj[0]->term_id, 'orderby' => 'name', 'hierarchical' => 0, 'show_option_none' => "Select $tax_name" ) ); ?>

        </div>
    <?php if ( current_user_can( $taxonomy->cap->edit_terms ) ) : 
            // removed code to add terms here dynamically, because doing so added a checkbox above the newly added drop menu, the drop menu would need to be re-rendered dynamically to display the newly added term ?>
        <?php endif; ?>

        <p><a href="<?php echo site_url(); ?>/wp-admin/edit-tags.php?taxonomy=<?php echo $tax_name ?>&post_type=YOUR_POST_TYPE">Add New</a></p>
    </div>
    <?php
}

The one issue I'm having (as indicated by my comments in the above function), is that I cannot use the WP core add term/category feature in the meta box, because it defaults to dynamically adding new terms as a checkbox above my select menu. In order to work with a select menu, the menu will need to be re-rendered after the new term is added. Something I'm sure I could figure out but will leave for another day.

Does this works for custom taxonomies?

I have my $args like this:

$args = array(
        'labels'                     => $labels,
        'hierarchical'               => true,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => true,
        'show_in_rest'               => true,
        'meta_box_cb'                => 'drop_cat'
    );

But is not changing anything in the editor, I'm still getting the default UI

the above code have some issues on me, let me put the right code that worked for me

 'meta_box_cb' => 'drop_cat',

your category definition , then put this code in here

remember to put

function drop_cat( $post, $box ) {
    if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) {
        $args = array();
    } else {
        $args = $box['args'];
    }
    $r = wp_parse_args( $args, $defaults );
    $tax_name = esc_attr( $r['taxonomy'] );
    $taxonomy = get_taxonomy( $r['taxonomy'] );
    ?>
    <div id="taxonomy-<?php echo $tax_name; ?>" class="categorydiv">

    <?php //took out tabs for most recent here ?>

        <div id="<?php echo $tax_name; ?>-all">
            <?php
            $name = ( $tax_name == 'category' ) ? 'post_category' : 'tax_input[' . $tax_name . ']';
            echo "<input type='hidden' name='{$name}[]' value='0' />"; // Allows for an empty term set to be sent. 0 is an invalid Term ID and will be ignored by empty() checks.
            ?>
            <ul id="<?php echo $tax_name; ?>checklist" data-wp-lists="list:<?php echo $tax_name; ?>" class="categorychecklist form-no-clear">
                <?php //wp_terms_checklist( $post->ID, array( 'taxonomy' => $tax_name, 'popular_cats' => $popular_ids ) ); ?>
            </ul>

            <?php $term_obj = wp_get_object_terms($post->ID, $tax_name ); //_log($term_obj[0]->term_id) 
          
            ?>
            <?php wp_dropdown_categories( array( 'taxonomy' => $tax_name, 'option_none_value' => 0 ,'id'=>$term_obj[0]->term_id, 'value_field'=> 'slug' , 'hide_empty' => 0, 'name' => "{$name}[]", 'selected' => $term_obj[0]->slug, 'orderby' => 'name', 'hierarchical' => 0, 'show_option_none' => "Select $tax_name" ) ); ?>

        </div>
    <?php if ( current_user_can( $taxonomy->cap->edit_terms ) ) : 
            // removed code to add terms here dynamically, because doing so added a checkbox above the newly added drop menu, the drop menu would need to be re-rendered dynamically to display the newly added term ?>
        <?php endif; ?>

        <p><a href="<?php echo site_url(); ?>/wp-admin/edit-tags.php?taxonomy=<?php echo $tax_name ?>&post_type=YOUR_POST_TYPE">Add New</a></p>
    </div>
    <?php
    }

I am also facing a challenge, my code here is meant to register a user taxonomy and display a dropdown for the terms on the user edit page... unfortunately, is displaying blank. how can I fix this?

<?php
// Register Custom Taxonomy
function custom_taxonomy() {

  $labels = array(
    'name'                       => _x( 'Departments', 'Departments Name', 'text_domain' ),
    'singular_name'              => _x( 'Department', 'Department Name', 'text_domain' ),
    'menu_name'                  => __( 'Departments', 'text_domain' ),
    'all_items'                  => __( 'All Departments', 'text_domain' ),
    'parent_item'                => __( 'Parent Department', 'text_domain' ),
    'parent_item_colon'          => __( 'Parent Department:', 'text_domain' ),
    'new_item_name'              => __( 'New Department Name', 'text_domain' ),
    'add_new_item'               => __( 'Add Department', 'text_domain' ),
    'edit_item'                  => __( 'Edit Department', 'text_domain' ),
    'update_item'                => __( 'Update Department', 'text_domain' ),
    'view_item'                  => __( 'View Department', 'text_domain' ),
    'separate_items_with_commas' => __( 'Separate department with commas', 'text_domain' ),
    'add_or_remove_items'        => __( 'Add or remove departments', 'text_domain' ),
    'choose_from_most_used'      => __( 'Choose from the most used', 'text_domain' ),
    'popular_items'              => __( 'Popular Departments', 'text_domain' ),
    'search_items'               => __( 'Search Departments', 'text_domain' ),
    'not_found'                  => __( 'Not Found', 'text_domain' ),
    'no_terms'                   => __( 'No departments', 'text_domain' ),
    'items_list'                 => __( 'Departments list', 'text_domain' ),
    'items_list_navigation'      => __( 'Departments list navigation', 'text_domain' ),
  );
  $args = array(
    'labels'                     => $labels,
    'hierarchical'               => true,
    'public'                     => true,
    'show_ui'                    => true,
    'show_admin_column'          => true,
    'show_in_nav_menus'          => true,
    'show_tagcloud'              => true,
  );
  register_taxonomy( 'departments', 'user', $args );

}
add_action( 'init', 'custom_taxonomy', 0 );

  /**
 * Admin page for the 'departments' taxonomy
 */
function cb_add_departments_taxonomy_admin_page() {

  $tax = get_taxonomy( 'departments' );

  add_users_page(
    esc_attr( $tax->labels->menu_name ),
    esc_attr( $tax->labels->menu_name ),
    $tax->cap->manage_terms,
    'edit-tags.php?taxonomy=' . $tax->name
  );

}
add_action( 'admin_menu', 'cb_add_departments_taxonomy_admin_page' );


/**
 * Unsets the 'posts' column and adds a 'users' column on the manage departments admin page.
 */
function cb_manage_departments_user_column( $columns ) {

  unset( $columns['posts'] );

  $columns['users'] = __( 'Users' );

  return $columns;
}
add_filter( 'manage_edit-departments_columns', 'cb_manage_departments_user_column' );

/**
 * @param string $display WP just passes an empty string here.
 * @param string $column The name of the custom column.
 * @param int $term_id The ID of the term being displayed in the table.
 */
function cb_manage_departments_column( $display, $column, $term_id ) {

  if ( 'users' === $column ) {
    $term = get_term( $term_id, 'departments' );
    echo $term->count;
  }
}
add_filter( 'manage_departments_custom_column', 'cb_manage_departments_column', 10, 3 );



/**
 * @param object $user The user object currently being edited.
 */
function cb_edit_user_department_section( $user ) {
  global $pagenow;

  $tax = get_taxonomy( 'departments' );

  /* Make sure the user can assign terms of the departments taxonomy before proceeding. */
  if ( !current_user_can( $tax->cap->assign_terms ) )
    return;

  /* Get the terms of the 'departments' taxonomy. */
  $terms = get_terms( 'departments', array( 'hide_empty' => false ) ); ?>

  <h3><?php _e( 'Departments' ); ?></h3>

 <?php }

/*fucntion to display taxonomy terms in a dropdown*/
function cb_taxonomy_select_meta_box($user, $box) 
{


  $defaults = array('departments' => 'departments');
  
  if (!isset($box['args']) || !is_array($box['args']))
      $args = array();
  else
      $args = $box['args'];
  
  extract(wp_parse_args($args, $defaults), EXTR_SKIP);
  
  $tax = get_taxonomy($departments);
  $selected = wp_get_object_terms($user->ID, $departments, array('fields' => 'ids'));
  $hierarchical = $tax->hierarchical;
  ?>
  <div id="departments-<?php echo $departments; ?>" class="selectdiv">
    <?php 
      if (current_user_can($tax->cap->edit_terms)): 
        if ($hierarchical) {
          wp_dropdown_categories(array(
             'departments' => $departments,
             'class' => 'widefat',
           'hide_empty' => 0, 
             'name' => "tax_input[$departments][]",
             'selected' => count($selected) >= 1 ? $selected[0] : '',
             'orderby' => 'name', 
             'hierarchical' => 1, 
             'show_option_all' => " "
          ));
        } else {?>
          <select name="<?php echo "tax_input[$departments][]"; ?>" class="widefat">
            <option value="0"></option>
            <?php foreach (get_terms($departments, array('hide_empty' => false)) as $term): ?>
              <option value="<?php echo esc_attr($term->slug); ?>" <?php echo selected($term->term_id, count($selected) >= 1 ? $selected[0] : ''); ?>><?php echo esc_html($term->name); ?></option>
            <?php endforeach; ?>
          </select>
        <?php 
        }
      endif; 
    ?>
  </div>
  <?php } /*end of code which should display the dropdown*/

add_action( 'show_user_profile', 'cb_edit_user_department_section' );
add_action( 'edit_user_profile', 'cb_edit_user_department_section' );
add_action( 'user_new_form', 'cb_edit_user_department_section' );


/**
 * @param int $user_id The ID of the user to save the terms for.
 */
function cb_save_user_department_terms( $user_id ) {

  $tax = get_taxonomy( 'departments' );

  /* Make sure the current user can edit the user and assign terms before proceeding. */
  if ( !current_user_can( 'edit_user', $user_id ) && current_user_can( $tax->cap->assign_terms ) )
    return false;

  $term = $_POST['departments'];

  /* Sets the terms (we're just using a single term) for the user. */
  wp_set_object_terms( $user_id, $term, 'departments', false);

  clean_object_term_cache( $user_id, 'departments' );
}

add_action( 'personal_options_update', 'cb_save_user_department_terms' );
add_action( 'edit_user_profile_update', 'cb_save_user_department_terms' );
add_action( 'user_register', 'cb_save_user_department_terms' );

/**
 * @param string $username The username of the user before registration is complete.
 */
function cb_disable_departments_username( $username ) {

  if ( 'departments' === $username )
    $username = '';

  return $username;
}
add_filter( 'sanitize_user', 'cb_disable_departments_username' );



/**
 * Update parent file name to fix the selected menu issue
 */
function cb_change_parent_file($parent_file)
{
    global $submenu_file;

    if (
      isset($_GET['taxonomy']) && 
      $_GET['taxonomy'] == 'departments' &&
      $submenu_file == 'edit-tags.php?taxonomy=departments'
    ) 
    $parent_file = 'users.php';

    return $parent_file;
}
add_filter('parent_file', 'cb_change_parent_file');


?> ```
Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top