Would you like to add new custom fields to your WordPress posts, pages, and categories? Now you can easily do this with the WordPress custom taxonomy system. For example, if you have a blog on movie reviews, you may want to add the fields Actors and Genre to each of your posts.
What is less clear, however, is how you can expand your WordPress admin interface, so that users can easily enter in these new custom fields. WordPress 2.8+ will only include an input interface for custom taxonomies associated with posts. In addition, this input interface is the standard tag interface, where you must type in the new fields as plain text.
If you are looking for a drop-down menu, or a radio button list, you are out of luck.
Here, we consider how you can flexibly expand your WordPress post interface and style your custom taxonomy input panel however you want.
1. Create Your WordPress Custom Taxonomy
First, we create a simple test attribute called theme and we associate it with our WordPress posts. We add three initial terms to our new theme attribute – Beauty, Halloween, and Dragons.
Note that the hierarchical argument simply refers to whether your new theme attribute is a hierarchical structure, such as your WordPress categories, or whether it is flat, such as your WordPress tags.
The hierarchical argument does not currently affect the input interface of your new attribute. As of WordPress 2.8, the normal tag input interface will be used for all custom taxonomy attributes. To restyle the custom taxonomy input interface, you must use the add_meta_box command.
add_action( 'init', 'create_theme_taxonomy', 0 ); function create_theme_taxonomy() { if (!taxonomy_exists('theme')) { register_taxonomy( 'theme', 'post', array( 'hierarchical' => false, 'label' => __('Theme'), 'query_var' => 'theme', 'rewrite' => array( 'slug' => 'theme' ) ) ); wp_insert_term('Beauty', 'theme'); wp_insert_term('Dragons', 'theme'); wp_insert_term('Halloween', 'theme'); } }
2. Styling Your Custom Taxonomy Input
To add input menus to your WordPress post interface, you want to use the WordPress add_meta_box command. In the example code below, we add a new custom field called Theme into our WordPress post interface. Simply include the code into your functions.php theme or plugin file.
function add_theme_box() { add_meta_box('theme_box_ID', __('Theme'), 'your_styling_function', 'post', 'side', 'core'); } function add_theme_menus() { if ( ! is_admin() ) return; add_action('admin_menu', 'add_theme_box'); } add_theme_menus();
The add_meta_box function adds your_styling_function to the WordPress blog system so that it gets called whenever the Edit Post screen is rendered. You can use the same function to add input code to Edit Page and Edit Link screens.
The example your_styling_function below will add a drop-down menu to your blog Edit Post screen, containing all the current terms on your theme custom taxonomy.
// This function gets called in edit-form-advanced.php function your_styling_function($post) { echo '<input type="hidden" name="taxonomy_noncename" id="taxonomy_noncename" value="' . wp_create_nonce( 'taxonomy_theme' ) . '" />'; // Get all theme taxonomy terms $themes = get_terms('theme', 'hide_empty=0'); ?> <select name='post_theme' id='post_theme'> <!-- Display themes as options --> <?php $names = wp_get_object_terms($post->ID, 'theme'); ?> <option class='theme-option' value='' <?php if (!count($names)) echo "selected";?>>None</option> <?php foreach ($themes as $theme) { if (!is_wp_error($names) && !empty($names) && !strcmp($theme->slug, $names[0]->slug)) echo "<option class='theme-option' value='" . $theme->slug . "' selected>" . $theme->name . "</option>\n"; else echo "<option class='theme-option' value='" . $theme->slug . "'>" . $theme->name . "</option>\n"; } ?> </select> <?php }
Lines 4-5 – Add security nonce check.
Line 9 – We use the hide_empty=0 argument for the get_terms function so that all theme choices will be returned, even the ones that have not yet been assigned to any post.
Line 15 – We use the wp_get_object_terms function to get the theme currently associated with our post so that we may pre-select it in our drop-down menu.
Lines 17-25 – Render our drop-down menu, populating it with our theme names.
Note – On lines 22 and 24, we are now setting the theme-option value to $theme->slug. As pointed out by Adam in the comments section, the taxonomy object slug is unique (unlike its name), and this will prevent duplicate taxonomy terms from being created.
Note that when you add your new drop-down menu box, the old tag input box will still appear. To only include one input box, use the remove_meta_box command as suggested by Leo Mysor in the comments section below.
remove_meta_box('tagsdiv-theme','post','core');
Note – For non-hierarchical taxonomies (like tags) you want to use tagsdiv-{$taxonomy_name}, e.g. tagsdiv-theme. For hierarchical taxonomies (like categories) you want to use {$taxonomy_name}div, e.g. themediv.
You can add the remove_meta_box command before your add_meta_box statement.
Alternatively, you can register your custom taxonomy attribute to something other than ‘post’. In the code example below, we register our theme custom taxonomy to shiba_post, which gets rid of the standard tag input box in the Edit Post screen.
register_taxonomy( 'theme', 'shiba_post', array( 'hierarchical' => false, 'label' => __('Theme'), 'query_var' => 'theme', 'rewrite' => array( 'slug' => 'theme' ) ) );
However, as pointed out by Leo, this also removes your taxonomy tab from the Posts menu and makes it difficult for others to add new items to your taxonomy.
3. Saving Your New Inputs
Now, we can insert any input panel we want for our custom taxonomy, however, we still need a way to save those input values. This can be achieved with the save_post WordPress hook. This hook allows you to execute a function of your choice when a WordPress post gets saved. There are similar hooks for saving pages and links.
Just add the save_post hook to your existing add_theme_menus function. For example, the code below registers the save_taxonomy_data function with the WordPress blog system so that it gets executed whenever a WordPress post is saved or updated.
function add_theme_menus() { if ( ! is_admin() ) return; add_action('admin_menu', 'add_theme_box'); /* Use the save_post action to save new post data */ add_action('save_post', 'save_taxonomy_data'); }
Now, you just need to specify your save_taxonomy_data function. We can adapt our own save function from the add_meta_data example on WordPress.org.
function save_taxonomy_data($post_id) { // verify this came from our screen and with proper authorization. if ( !wp_verify_nonce( $_POST['taxonomy_noncename'], 'taxonomy_theme' )) { return $post_id; } // verify if this is an auto save routine. If it is our form has not been submitted, so we dont want to do anything if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id; // Check permissions if ( 'page' == $_POST['post_type'] ) { if ( !current_user_can( 'edit_page', $post_id ) ) return $post_id; } else { if ( !current_user_can( 'edit_post', $post_id ) ) return $post_id; } // OK, we're authenticated: we need to find and save the data $post = get_post($post_id); if (($post->post_type == 'post') || ($post->post_type == 'page')) { // OR $post->post_type != 'revision' $theme = $_POST['post_theme']; wp_set_object_terms( $post_id, $theme, 'theme' ); } return $theme; }
Lines 4-6 – First we do a nonce check to ensure that the function is being called by our very own your_styling_function. Make sure that the taxonomy_noncename and taxonomy_theme terms match those that were created earlier, on lines 4-5 in your_styling_function.
Lines 9-10 – Take no action for auto-saves.
Lines 14-20 – Check that the current user has proper permissions to edit posts.
Lines 23-28 – Associates our post with the new theme taxonomy data. It is important to do a post_type check here, because this function will also get called on post revision objects.
As pointed out by Angelia, this results in double counting the newly added taxonomy relationship.
4. Getting a Taxonomy Term Count
If you want to get the count of a particular taxonomy term, i.e., the number of objects that it is associated with, you can easily extract that figure from the WordPress term_taxonomy database.
Just add the count code into the foreach $themes loop.
global $wpdb; foreach ($themes as $theme) { $count = $wpdb->get_var( $wpdb->prepare( "SELECT count FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $theme->term_taxonomy_id) ); /* Your code here to display the count ... */ }
While registering your custom taxonomy, you can link an update_count_callback function to it. This function will get called every time any term in your taxonomy gets a count update. This allows you to control what actually gets stored in the count column of your custom taxonomy terms.
$args = array( 'hierarchical' => false, 'update_count_callback' => 'test_taxonomy_count', 'label' => __('Theme'), 'query_var' => 'theme', 'rewrite' => array( 'slug' => 'theme' ) ) register_taxonomy( 'theme', 'post', $args); // This test count function just does the default WordPress operations function test_taxonomy_count($terms) { global $wpdb; $terms = array_map('intval', $terms); foreach ( (array) $terms as $term) { $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term) ); $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); } }
5. All Done!
You can use the same code to style your custom taxonomy input panels for pages and links. Just change the post attribute to page or link when calling add_meta_box and use the save_page, edit_link, and add_link hooks instead of save_post.
You can also add new fields to your WordPress blog categories using a similar system.
Mike Carter says
Hi ShibaShake —
Your tutorials must be the most complete on the net. I’m just getting into taxonomies (been using custom fields), and the possibilities are mind-boggling. Now, this is an off-the-wall question, but is there a way in CODE to convert a custom field value to a taxonomy term value? I know Scribu has a plugin for this, but I need for it to happen in theme itself. I think I’m asking if a value can be assigned to a taxonomy term; that is, can I do something like make $taxonomyvalue = $customfieldvalue and then use that taxonomy value for the rest of my code?
If not possible, or too nuts, let me know!
ShibaShake says
That sounds doable, although I am not sure in what context you would need to do that.
Do you have an existing custom field that is already populated with data? If so, I would just do a one-off conversion.
Amy says
Thanks very much for this really interesting post!
Unfortunately, I was silly enough to register a ‘test’ taxonomy. And now can’t figure out how to remove it! Any help would be hugely appreciated.
Thanks,
Amy
ShibaShake says
http://core.trac.wordpress.org/ticket/12629
Paul says
Hi ShibaShake,
I have a few questions, hoping u don’t mind to help out.
Do u still keep this post up to date ?
I mean want to take your code and implement it on my project, but I’m afraid the what-if you don’t really plan to keep it on. I myself am not a php guru so if I copy yours and use it, I won’t be able to keep it up to date later.
I noticed in you code above, the is_taxonomy() is already deprecated, but u still have it that way.
and also, would u please write up a post on how to make radio check box custom taxonomy input panel ?
Anyway, Thanks for this post, it’s very useful, a lot of people have linked to this page.
ShibaShake says
Hello Paul,
Unfortunately, it is simply not possible to go through all previous posts every time there is a WordPress release. However, if updates get pointed out to me in comments, as you have done, I will usually go back and update the post.
In terms of radio boxes, all you need to do is use the radiobox form inputs in your_styling_function instead of the dropdown menu form inputs. Something like this –
Paul says
Thanks a lot,ShibaShake, you are the best !
Manny Fleurmond says
One thing that I can’t seem to work with your code: if the taxonomy is set to work like a category ie hierarchical, then the meta box wordpress creates automatically doesn’t get removed when you call the remove_meta_box function. Would you happen to know if there is a way around this?
ShibaShake says
Thanks for posting the question on WP stackexchange and thanks to Jan Fabry for his great answer.
http://wordpress.stackexchange.com/questions/6183/how-do-you-remove-a-category-style-hierarchical-taxonomy-metabox
JR Oakes says
I took some of this code and created a plugin that allows you to enter a Post Type and a Taxonomy on the settings screen. Then the Taxonomy Box on the post edit screen will be changes to a drop-down select field that allows you to select only one value.
If you can add anything to make it better I am cool with that. I figured since I had to spend the time to get this working I might as well make it a plugin.
ShibaShake says
Thanks for sharing! This is one of the things I like most about the WP community.
Griff says
JR Oakes – absolutely love that Plugin you credted. I was wondering if there was a way to have the title of the Meta Box appear as the Title case version of the taxonomy? For example, a taxonomy for Manufacturers appears with title “manufacturers”. I had a look at the code, but couldn’t really figure it out 🙂
Manny Fleurmond says
Thanks for the review! I did a mashup of this code along with the “How To Create A Better Meta Box In WordPress (Part 2)(http://www.deluxeblogtips.com/2010/05/howto-meta-box-wordpress.html) code. My mashup can be found here:
http://pastebin.com/93eydx1k
Basically, I modified your code and added a checkbox mode to select multiple terms like you would tags.
Jacobo Polavieja says
Hello Shibashake!
Thanks a lot for the tutorial. This blog is full of valuable resources for WordPress.
I’ve tried your code and have experimented a weird thing, so I hoped you could help me. If I paste your code directly into my theme’s functions.php all works well. However, if I try to include in a plugin I’m developing two things happen:
1. The “Themes” dropwdown appears with the “None” option, and below it there are two completely blank (which is weird, as if it was three at least I could thing there’s a problem retrieveing the taxonomy names, but it is just to blank options).
2. The “Themes” NORMAL box (in the sense of the usual tags display format) doesn’t appear (it does when I paste the code into functions.php), although I haven’t still used the tip of using “remove_meta_box(‘tagsdiv-theme’,’post’,’core’);”.
If you could have a look it’d be of great help as you seem the only one filling this necessity of ours of having custom admin displays
The plugin code: http://wordpress.pastebin.com/78nW5yYC
A screenshot: http://img291.imageshack.us/img291/4453/dropdownj.jpg
Hope you can help, because after looking at it lots of times I can’t get what I may be doing wrong.
Thanks for all this blog, is amazing.
ShibaShake says
Hmmm, the first thing I would do is clean-up the class initialization function. Don’t just paste the whole tutorial code in there because you are also doing calls to add_action-init in your plugin proper. In general, you only want to have one init function and one admin_init function so I would integrate those.
Also, I would take the tutorial functions out of the initialization process and put them in the class.
I am not sure if this will solve your issues but the code is difficult to debug in its current structure.
Are you trying to add the custom taxonomy to posts? or to your new custom post type? If you are adding it to your new custom post type then you should use your custom post type name instead of ‘post’.
Good luck!
vilo says
Great tutorial! I have one question that wasn’t mentioned. I am using p2 template that allows blog editing by multiple users from the front end (not in admin). How do I get taxonomy input form to the front so when user is submitting his post he can choose some other options from the pulldown menus of taxonomies? Even better how could this be hooked to the jquery so your form could be used from the front for article ratings, opinion selections and such. Any direction would be much appreciated.
ShibaShake says
I am not familiar with the inner workings of the p2 theme. You can probably get the best help from the p2 theme creators.
Jacobo Polavieja says
Hello again and thanks for taking the time to look at the code and answer.
Following your advices I’ve cleaned up the code a litte bit. I’ve moved the taxonomies onto functions.php, as they are more likely to change than the custom types, which I’ve left on the plugin file.
The only problem that’s left now is that I’d like to have the custom taxonomy display on both my ‘post’ types and my custom post types.
I’ve tried to pass the add_meta_box function two parameters like this:
add_meta_box(‘main_country_box_ID’, __(‘Main country’), ‘your_styling_function’, array( ‘post’, ‘custom_post_type’ ), ‘side’, ‘core’);
But WordPress throws a “Illegal offset type” warning and doesn’t show the box in either type.
Other options I’ve tried have been:
A) Doing two calls:
add_meta_box(‘main_country_box_ID’, __(‘Main country’), ‘your_styling_function’, ‘post’, ‘side’, ‘core’);
add_meta_box(‘main_country_box_ID’, __(‘Main country’), ‘your_styling_function’, ‘custom_post_type’, ‘side’, ‘core’);
—> The box is displayed JUST on the “post” type editing screen, not on my custom post type’s.
B) Trying to get it to work just on the custom post type:
add_meta_box(‘main_country_box_ID’, __(‘Main country’), ‘your_styling_function’, ‘custom_post_type’, ‘side’, ‘core’);
—> The box doesn’t get displayed in either type
All of this, of course, with the taxonomy previously registered for both types in your “create_nameoftaxonomy_taxonomy” function:
register_taxonomy( ‘main_country’, array( “post”, “new” ), array( ‘hierarchical’ => false, ‘label’ => __(‘Main country’), ‘query_var’ => ‘main_country’, ‘rewrite’ => array( ‘slug’ => ‘main_country’ ) ) );
All code at: http://wordpress.pastebin.com/YQtP7AMg
I can’t find what may be conflicting. If you could provide any help it’d be great as I’m so close!
Thanks a lot for everything, have subscribed to your blog, one of the most interesting ones in the WordPress world.
ShibaShake says
Try doing –
Put in the name of your custom post type, i.e. ‘new’ instead of ‘custom_post_type’.
Jacobo Polavieja says
That worked!
The way it is put on the Codex doesn’t really clarify that point much, or at least I didn’t get it well.
Thanks a lot for helping me through this, very kind on your part. I now have all fully working!
Many many thanks. Cheers!