Jess G's BlogTutorials and Random Thoughts

How To: Working with the WordPress Metadata API

Quick tutorial on how to make use of the Metadata API that became available in WordPress 2.9. Yeah, it's been around that long. The Metadata API essentially allows you to create a new table for adding post-type-specific meta-data, which can be pretty handy if you don't wish to clutter up the $wpdb->postmeta table with meta for other post-types.

1. Table Schema

We start with our table schema:

CREATE TABLE IF NOT EXISTS `wp_projectmeta` (
 `meta_id` bigint(20) NOT NULL AUTO_INCREMENT,
 `project_id` bigint(20) NOT NULL,
 `meta_key` varchar(255) CHARACTER SET latin1 NOT NULL,
 `meta_value` longtext CHARACTER SET latin1 NOT NULL,
 PRIMARY KEY (`meta_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

This is where the WordPress Codex documentation isn't very clear. The section on the schema makes mention of an "object_id" field, which is like the post_id field in the $wpdb->postmeta table. The ambiguity of the documentation might trip a person up when initially creating the table. What the documentation should say is: {$post_type}_id. In this example, I have "project_id." The "project"-part is the post-type I want the table associated with.

2. Initialization

This step can vary, depending on if you're using this for a theme or a plugin. You need to add a new table property to $wpdb. For a plugin, you might want to do this on the init hook:

<?php
add_action('init', 'my_plugin_init');
function my_plugin_init()
{
    global $wpdb;

    $wpdb->projectmeta = $wpdb->prefix . 'projectmeta';
}
?>

For a theme, place it directly in the functions.php file.

<?php
/**
 * functions.php
 */
global $wpdb;
$wpdb->projectmeta = $wpdb->prefix . 'projectmeta';
...

I've run into issues using the init hook in a theme for this purpose. The same could hold true for plugins.

  1. Usage There are four functions for working with the new meta data table: add_metadata, get_metadata, update_metadata, and delete_metadata. You can review the documentation on all four functions in the Codex, but the usage is:
<?php
  get_metadata('project', $post_ID, 'your_meta_key', 'Your Meta Value');
  add_metadata('project', $post_ID, 'your_meta_key', 'Your Meta Value');
  update_metadata('project', $post_ID, 'your_meta_key', 'Your Meta Value');
  delete_metadata('project', $post_ID, 'your_meta_key', 'Your Meta Value');
?>

Example

<?php
add_action('save_post', 'save_custom_data');
function save_custom_data($post_ID)
{
    // do nonce checks here
    // do autosave/revision checks here

    // don't forget to sanitize your meta data before DB insertion.

    // depending if the meta_key is unique
    if (!add_metadata('project', $post_ID, 'meta_key', 'meta_value', true)) {
        update_metadata('project', $post_ID, 'meta_key', 'meta_value');
    }

    // or if you don't mind an array of meta_keys
    add_metadata('project', $post_ID, 'meta_key', 'meta_value');
}

That's it! One thing of note is this line from the Codex on the description for add_metadata():

add_metadata() allows for metadata to be added to objects in WordPress. This is a generic, low level function and should not be used directly by plugins or themes. Instead, use the corresponding meta functions for the object type you're working with...

This is inaccurate imo because we're working with a custom post-type. Obviously, if we were working with one of the builtin post-types, we would use the corresponding functions for that post-type. A good example is that add_post_meta() uses add_metadata() as the return value, but passes 'post' for the $object parameter. It just seems more semantically correct to use separate tables for your custom post-type's metadata!