Introducing Term Meta Data In WordPress And How To Use Them

About The Author

Thomas fell in love with WordPress as a freelance developer in 2010 and now focuses on his ad management plugin Advanced Ads and website monetization for his … More about Thomas ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Term meta data allows you to save meta values for terms in a similar way to post meta data. 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. Thomas Maier knows 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. In this tutorial, Thomas will show you how you can edit, update and retrieve these meta data for terms.

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 data
  • update_term_meta(): updates existing meta data
  • delete_term_meta(): deletes meta data
  • get_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.

create term form with meta data field
The form to create a new term in the custom taxonomy should now look like this. (View large version)

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 to false.

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 to false 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:

term list with meta data
Term list with meta data table. (View large version)

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

Excerpt image: Nikolay Bachiyski

Further Reading

Smashing Editorial (dp, jb, ml, og, mrn)