How to enable checkbox lists for non-hierarchical taxonomies in WordPress

WordPress’ taxonomy features are one of the most powerful aspects of the platform, giving you the capabilities to group posts (including custom post types) in pretty much as many ways as you can think of.

There are two types of WordPress taxonomy – hierarchical and non-hierarchical. In case it’s not obvious from the name, a hierarchical taxonomy has a hierarchy – that is, you can create terms that are children of a parent term (and even children of those children) – standard post categories in WordPress are an “out of the box” example of a hierarchical taxonomy.

A non-hierarchical taxonomy has no parent/child structure, and is basically just a “flat” list of terms. Post tags are the “out of the box” example here.

When you’re developing a plugin for WordPress (or maybe even a theme) you may find that you have a need to create custom taxonomies – for example, if creating a plugin for a property/real estate website, you may have a custom taxonomy for “Property Type”, that contains terms like “House”, “Apartment” etc.

It’s at this point that you may run into a bit of an annoyance with the default WordPress user interface…

A comparison between the default WordPress user interface for hierarchical and non-hierarchical taxonomies

The example given above (Property Type) is non-hierarchical. By default, this means that an administrator creating or editing a new post with that taxonomy needs to manually type in the relevant Property Type(s) in the box and wait for it to auto-complete, or click the “choose from the most used” option and hope that the ones they want are in there.

It makes a lot more sense, in some circumstances at least, for non-hierarchical taxonomies to also use the same “checkbox list” user interface used for hierarchical taxonomies, as shown above. This way, the user is presented with a list of all of the available options without having to recall them from memory, and can still add new ones if necessary.

The good news is, that starting from WordPress 3.7 (which was released quite a while ago, at the time of writing WordPress is at version 4.9), it’s possible to tell WordPress to use the checkbox UI for non-hierarchical taxonomies – with a caveat.

Let’s continue with the Property Type example. Here’s our (basic) code for registering the custom taxonomy for it:

add_action( 'init', 'create_property_type_taxonomy' );

function create_property_type_taxonomy() {
    register_taxonomy(
        'propertytype', // the slug for the new taxonomy
        'property', // the slug of the custom post type that this taxonomy applies to
        array(
            'label' => __( 'Type' ),
            'rewrite' => array( 'slug' => 'property-type' ),
            'hierarchical' => false,
        )
    );
}

This creates a non-hierarchical taxonomy, with the default UI as shown above. To switch the UI over to the checkbox style above, we just need one more line:

add_action( 'init', 'create_property_type_taxonomy' );

function create_property_type_taxonomy() {
    register_taxonomy(
        'propertytype', // the slug for the new taxonomy
        'property', // the slug of the custom post type that this taxonomy applies to
        array(
            'label' => __( 'Type' ),
            'rewrite' => array( 'slug' => 'property-type' ),
            'hierarchical' => false,

            'meta_box_cb' => 'post_categories_meta_box',
        )
    );
}

The newly-added meta_box_cb argument tells WordPress which function to use to generate the meta box for displaying the taxonomy on the post edit screen. You could use this to create your own meta box style, but here we are just telling it to use the existing post_categories_meta_box function, which is what displays the checkbox style UI for hierarchical taxonomies.

So, all good, right?

No, not quite. Even though our taxonomy is non-hierarchical, if the user selects the “Add new” option, they are shown the box to add in a new term, but also a drop-down select menu for choosing the parent for this new term. If the user selects a parent, WordPress will add the new term as a child of that parent, but because the taxonomy is non-hierarchical it will not be selectable, either with the checkboxes or with the default UI.

You could just tell your users not to select a parent, but a good UI will not give the user options that they shouldn’t use, so we need to remove that parent menu.

Fortunately, version 4.4 of WordPress introduced a filter – post_edit_category_parent_dropdown_args – that could be used to control the parent terms shown in these meta boxes. It’s designed to let the developer change the terms listed, for example excluding certain categories, or only showing “top level” parent terms and not their descendants. There is no control that is designed to stop the menu being shown at all, but there is one that allows us to trick WordPress into hiding the parent drop-down select.

Here’s the filter that you need:

add_filter( 'post_edit_category_parent_dropdown_args', 'hide_parent_dropdown_select' );

function hide_parent_dropdown_select( $args ) {
    if ( 'propertytype' == $args['taxonomy'] ) {
        $args['echo'] = false;
    }
    return $args;
}

Why does this work? The echo argument is set to true by default, and makes WordPress echo the dropdown select into the meta box. By setting this to false, WordPress instead returns the HTML instead, so it doesn’t get rendered to the browser.

You’ll also notice that I’ve added a check into the filter that makes sure we’re only doing this for the ‘propertytype’ taxonomy – we don’t want this filter to remove the parent dropdown selector from all of the hierarchical taxonomies after all!

Update: Converting term IDs to term names

Something that I hadn’t banked on when I created this post originally was that, when sending the selected terms back to WordPress from the post editing screens, hierarchical taxonomies send them each term’s ID, whereas non-hierarchical taxonomies send the term names instead.

Because we’re now using the hierarchical UI for our non-hierarchical taxonomy, WordPress receives the term ID (as a string) and creates a new term with the ID as the name, losing the relationship with the original term in the process. Sounds confusing, and is.

To get around this, we need to take these ID strings and convert them to integers. To do this, we just need to hook into the admin_init action hook and convert them:

add_action( 'admin_init', 'convert_taxonomy_terms_to_integers' );

function convert_taxonomy_terms_to_integers() {
    $taxonomy = 'propertytype';
    if ( isset( $_POST['tax_input'][ $taxonomy ] ) && is_array( $_POST['tax_input'][ $taxonomy ] ) ) {
        $terms = $_POST['tax_input'][ $taxonomy ];
        $new_terms = array_map( 'intval', $terms );
        $_POST['tax_input'][ $taxonomy ] = $new_terms;
    }
}

And there you have it, all done. If you found this helpful, please let me know in the comments below.

Previous

WooCommerce: Use Product Images as Category Images

Next

WooCommerce: Purchase Order Payment Gateway

5 Comments

  1. Kaycie

    Thanks! This was helpful, but I found that it wouldn’t work with Gutenburg for me.

  2. WordPress really needs to isolate the “is it hierarchical” question from the “checkboxes or open text field” question. It shouldn’t be a “callback” option, it should be “use_checkboxes” and it should support both category-type and tag-type.

    There’s way too many incidental details tied to the current `hierarchical` flag. It leads us to hacky solutions like what you’ve posted, which don’t really work despite trying to satisfy a really obvious need.

    #hottakestenyearslate

    • gazchap

      Agree completely – makes me wonder if there’s a Trac ticket suggesting it, I’ll have a look.

  3. Note: This solution only works, if the given post type does not use Gutenberg (either support for `editor` is disabled or forced to use Classic Editor). Once Gutenberg is used, the meta box is rendered by React based on data from the REST API, and `post_edit_category_parent_dropdown_args` filter is not effective any more, also the `meta_box_cb` parameter for `register_taxonomy` is ignored. (See related issue here: https://github.com/WordPress/gutenberg/issues/13816 )

Leave a Reply

Your email address will not be published. Required fields are marked *

Privacy Policy & Powered by WordPress & Theme by Anders Norén