Introducing Term Meta Data In WordPress And How To Use Them
WordPress 4.4 introduced term meta data which allows you to save meta values for terms in a similar way to post meta data. This is a highly anticipated and logical addition to the WordPress system.
So far, the post and comment meta systems allowed us to add arbitrary data to posts and comments. This can be used to add ratings to comments, indicate your mood while you were writing a post, attach prices to product posts, and various other information you think is relevant to your content. As of the newest version of WordPress, meta data can now be added to terms which allows us to create features like default category thumbnails in a standardized way. This tutorial will show you how you can edit, update and retrieve these meta data for terms.
Under The Hood Of Term Meta Data
The logic behind term meta data is not new, already being used for posts, comments and users.
To enable meta data for terms, the new termmeta
table was introduced. It saves the combination of the term_id
, meta_key
and meta_value
, plus an incrementing meta_id
.
Meta Data Functions
Four new functions were introduced to handle create, read, update and delete (CRUD) operations for term meta data:
add_term_meta()
: adds the meta dataupdate_term_meta()
: updates existing meta datadelete_term_meta()
: deletes meta dataget_term_meta()
: retrieves the meta data
Under the hood, these functions use the same code that the corresponding functions for post meta data use as well.
How To Use Term Meta Data
In a recent project, I had to assign additional attributes to non-hierarchical terms, which was a great chance to test the new meta data feature.
The project used a custom post type to represent houses. The features of a house (e.g. TV, sofa, etc.) were terms in a custom taxonomy. The editor of the house information wanted to list the features based on a group that should not be a feature by itself. Hence, I decided to add this group using term meta data.
The taxonomy I used was house_feature
, but you can also use the existing category
or post_tag
taxonomies if you want to use term meta data with post categories or tags.
First, I created my feature taxonomy:
add_action('init', 'register_feature_taxonomy');
function register_feature_taxonomy() {
$labels = array(
'name' => _x( 'Features', 'taxonomy general name', 'my_plugin' ),
'singular_name' => _x('Features', 'taxonomy singular name', 'my_plugin'),
'search_items' => __('Search Feature', 'my_plugin'),
'popular_items' => __('Common Features', 'my_plugin'),
'all_items' => __('All Features', 'my_plugin'),
'edit_item' => __('Edit Feature', 'my_plugin'),
'update_item' => __('Update Feature', 'my_plugin'),
'add_new_item' => __('Add new Feature', 'my_plugin'),
'new_item_name' => __('New Feature:', 'my_plugin'),
'add_or_remove_items' => __('Remove Feature', 'my_plugin'),
'choose_from_most_used' => __('Choose from common Feature', 'my_plugin'),
'not_found' => __('No Feature found.', 'my_plugin'),
'menu_name' => __('Features', 'my_plugin'),
);
$args = array(
'hierarchical' => false,
'labels' => $labels,
'show_ui' => true,
);
register_taxonomy('house_feature', array('houses'), $args);
}
You should change the houses
post type in the last line to the one you are using, or just to post
if you want to take the new taxonomy out for a test run on default posts.
If you use the code from this article in your theme’s functions.php file or within a plugin, make sure that the text domain my_plugin
matches the text domain of your theme or plugin. The my_plugin
text domain would work in a plugin with a my_plugin
slug.
The groups I assigned the house features to were managed through settings. It can also be a simple array. For the sake of a short, but useful introduction into term meta, I am using the following global array to hold the available groups:
$feature_groups = array(
'bedroom' => __('Bedroom', 'my_plugin'),
'living' => __('Living room', 'my_plugin'),
'kitchen' => __('Kitchen', 'my_plugin')
);
Extending The Term Form
To assign meta data to terms, we need to extend the term edit form. The complicated part is that the hooks we need from now on are built dynamically.
Adding Meta Data With A New Term
In order to extend the form to add a term, we make use of the {$taxonomy}_add_form_fields
hook. For our house_feature
taxonomy it resolves into house_feature_add_form_fields
.
add_action( 'house_feature_add_form_fields', 'add_feature_group_field', 10, 2 );
function add_feature_group_field($taxonomy) {
global $feature_groups;
?><div class="form-field term-group">
<label for="featuret-group"><?php _e('Feature Group', 'my_plugin'); ?></label>
<select class="postform" id="equipment-group" name="feature-group">
<option value="-1"><?php _e('none', 'my_plugin'); ?></option><?php foreach ($feature_groups as $_group_key => $_group) : ?>
<option value="<?php echo $_group_key; ?>" class=""><?php echo $_group; ?></option>
<?php endforeach; ?>
</select>
</div><?php
}
This adds a select form right between the original form fields and the submit button.
To save the term meta we need to hook into the action, which is fired when the new term is being saved. This time we use the created_{$taxonomy}
hook.
add_action( 'created_house_feature', 'save_feature_meta', 10, 2 );
function save_feature_meta( $term_id, $tt_id ){
if( isset( $_POST['feature-group'] ) && ’ !== $_POST['feature-group'] ){
$group = sanitize_title( $_POST['feature-group'] );
add_term_meta( $term_id, 'feature-group', $group, true );
}
}
We get the term data from the $_POST
array sent with the form and save it using the new add_term_meta()
function. Like add_post_meta()
, it takes four arguments:
$term_id
: the id of the term,$meta_key
: the meta key,$meta_value
: the value,$unique
: whether the key can only be used once; defaults tofalse
.
I am setting $unique
to true, because I want each feature of the house to be listed in only one group.
Updating A Term With Meta Data
Even though they share similar features, adding a new term and updating an existing one is technically different in WordPress. Therefore, we need to add an update routine too.
We make use of the {$taxonomy}_edit_form_fields
hook to get the field for the group into the edit form.
add_action( 'house_feature_edit_form_fields', 'edit_feature_group_field', 10, 2 );
function edit_feature_group_field( $term, $taxonomy ){
global $feature_groups;
// get current group
$feature_group = get_term_meta( $term->term_id, 'feature-group', true );
?><tr class="form-field term-group-wrap">
<th scope="row"><label for="feature-group"><?php _e( 'Feature Group', 'my_plugin' ); ?></label></th>
<td><select class="postform" id="feature-group" name="feature-group">
<option value="-1"><?php _e( 'none', 'my_plugin' ); ?></option>
<?php foreach( $feature_groups as $_group_key => $_group ) : ?>
<option value="<?php echo $_group_key; ?>" <?php selected( $feature_group, $_group_key ); ?>><?php echo $_group; ?></option>
<?php endforeach; ?>
</select></td>
</tr><?php
}
In addition to the add form, we need to retrieve the existing term data using get_term_meta()
and pre-select it. This function has three arguments:
$term_id
: the id of the term,$key
: the key of the meta data,$single
: whether to return a single result; defaults tofalse
which would return an array.
Since I am expecting only one value to be returned, I set $single
to true
.
We now need to hook into edited_{$taxonomy}
to save the data.
add_action( 'edited_house_feature', 'update_feature_meta', 10, 2 );
function update_feature_meta( $term_id, $tt_id ){
if( isset( $_POST['feature-group'] ) && ’ !== $_POST['feature-group'] ){
$group = sanitize_title( $_POST['feature-group'] );
update_term_meta( $term_id, 'feature-group', $group );
}
}
We use update_term_meta()
to overwrite the existing value instead of adding a new set of meta data.
Displaying The Term Meta Data In The Term List
The meta data is saved now. Let’s display it in a new column in the term list table. First, let’s add the column and header to the table. Finding the correct hook to extend a table in the WordPress dashboard is again a bit tricky because of so many flexible hook names.
The pattern you can use here is manage_edit-{$taxonomy}_columns
, even though this is not exactly how the hook is defined.
add_filter('manage_edit-house_feature_columns', 'add_feature_group_column' );
function add_feature_group_column( $columns ){
$columns['feature_group'] = __( 'Group', 'my_plugin' );
return $columns;
}
To add the content into the column fields, you can use the hook pattern manage_{$taxonomy}_custom_column
.
add_filter('manage_house_feature_custom_column', 'add_feature_group_column_content', 10, 3 );
function add_feature_group_column_content( $content, $column_name, $term_id ){
global $feature_groups;
if( $column_name !== 'feature_group' ){
return $content;
}
$term_id = absint( $term_id );
$feature_group = get_term_meta( $term_id, 'feature-group', true );
if( !empty( $feature_group ) ){
$content .= esc_attr( $feature_groups[ $feature_group ] );
}
return $content;
}
Again, we use get_term_meta()
to retrieve the value and then simply attach it to the existing content. This would allow other developers to attach something else here as well.
It was in the nature of my project, that many terms shared the same value, so let’s make the group column sortable. We only need to add it to the sortable columns.
add_filter( 'manage_edit-house_feature_sortable_columns', 'add_feature_group_column_sortable' );
function add_feature_group_column_sortable( $sortable ){
$sortable[ 'feature_group' ] = 'feature_group';
return $sortable;
}
This is how the term list might look like with an extra column for term data:
Deleting Term Meta Data
If you remove a term, the corresponding meta data will also be removed, so you don’t need to clean up after yourself.
However, if you find yourself needing to remove term meta data, you can use the delete_term_meta()
function. It takes up to three arguments:
$term_id
: the id of the term$meta_key
: the meta key$meta_value
: the previous value
You only need to provide $meta_value
if you want to remove meta data with a specific value.
Retrieving Terms By Meta Value
Like with posts, you can also retrieve terms using the meta values. Set the meta_query
parameter when using get_terms()
or wp_get_object_terms()
.
With the following query I would retrieve all features in the kitchen
group.
$args = array(
'hide_empty' => false, // also retrieve terms which are not used yet
'meta_query' => array(
array(
'key' => 'feature-group',
'value' => 'kitchen',
'compare' => 'LIKE'
)
)
);
$terms = get_terms( 'house_feature', $args );
The syntax of meta_query
is the same as in WP_Query
, which allows you to make use of different operators for compare
, like NOT LIKE
, EXISTS
, or BETWEEN
.
Since I use meta data to group my terms, getting the result back ordered by the meta value would be really helpful. However, unlike with WP_Query
, you can’t use orderby => meta_value
yet and must sort the results after you retrieve them.
To display our terms and meta data, we iterate through the results and make use of get_term_meta()
again.
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ){
echo '<ul>';
foreach ( $terms as $term ) {
echo '<li>' . $term->name . ' (' . get_term_meta( $term->term_id, 'feature-group', true ) . ')' . '</li>';
}
echo '</ul>';
}
Conclusion
I know many projects that already save meta information for custom taxonomies. A lot of them are probably going to update and use the new meta data logic once WordPress 4.4 is widely used.
The last thing I am missing now in the taxonomy roadmap is to be able to set meta data for a specific term–object relationship. Let me know if you can relate to that in the comments.
Resources
get_terms
in the WordPress Codex- Taxonomy roundup on make.wordpress.org
Excerpt image: Nikolay Bachiyski
Further Reading
- Extending WordPress With Custom Content Types
- How To Add Custom Fields In A WordPress Comment Form
- RICG Responsive Images For WordPress
- Everything You Need To Know About Accelerated Mobile Pages