The WordPress metadata API is a simple way to store and retrieve information related to various objects in WordPress, such as posts, users, comments, and taxonomy terms. Out of the box, WordPress includes post meta, user meta, comment meta, and term meta, but what if you want metadata on other objects, such as custom objects provided by a plugin? Thankfully, the metadata API is actually quite simple to extend, allowing developers to easily register their own kind of metadata that is attached to their own, custom objects.
Before diving into how we can extend the metadata API, let’s have a quick refresher on the existing metadata functions available in WordPress.
For each object type, there are four primary functions used by developers:
- get
- add
- update
- delete
Posts
For post objects, we have the following metadata functions:
- get_post_meta()
- add_post_meta()
- update_post_meta()
- delete_post_meta()
Comments
For comment objects, we have the following metadata functions:
- get_comment_meta()
- add_comment_meta()
- update_comment_meta()
- delete_comment_meta()
Users
For user objects, we have the following metadata functions:
- get_user_meta()
- add_user_meta()
- update_user_meta()
- delete_user_meta()
Terms
For term objects, we have the following metadata functions:
- get_term_meta()
- add_term_meta()
- update_term_meta()
- delete_term_meta()
All of these functions work exactly the same way. For example, to get metadata, it would look like this:
$value = get_post_meta( $post_id, 'meta_key', true );
To update metadata:
$updated = update_user_meta( $user_id, 'meta_key', 'New Value' );
To delete metadata:
$deleted = delete_term_meta( $term_id, 'meta_key' );
To add a new row of metadata:
$added = add_post_meta( $post_id, 'meta_key', 'The new value', $unique = true );
Notice that they are all identical in everything but name. Each of these functions is actually just a wrapper for the general metadata functions:
- get_metadata()
- add_metadata()
- update_metadata()
- delete_metadata()
For example, get_post_meta() simple calls get_metadata() like this:
$value = get_metadata( 'post', $object_id, 'meta_key', $single = true );
And, update_term_meta() simple calls update_metadata() like this:
$updated = update_metadata( 'term', $object_id, 'meta_key', $single = true );
Extending the metadata API
We have refreshed our memory on what the standard metadata API functions look like, so now let’s see how we can extend these to interact with our own metadata.
The first thing you need to do is have an object type that metadata will be registered for. This could really be anything but let me provide you with a few examples.
In AffiliateWP we use a custom table for affiliate accounts. An affiliate is similar to a user account and we often want to store metadata for affiliates, much like is often done for user accounts. We extended the metadata API to provide support for affiliate meta.
In Easy Digital Downloads we use a custom table to keep track of customer records. We recently added a new customer meta API that extends the one in WordPress. This allows us to store metadata for customer records.
In Restrict Content Pro we use a custom table for subscription levels and payment records. Both of these needed to support custom metadata, so we added metadata tables that extend the WordPress API.
Other examples of object types that may need metadata could include invoices, sale receipts, photos, and so many more. Basically, if you register a custom table and do not rely on the core WordPress tables, it may behoove you to add a metadata layer as well.
There are several components involved with registering your own metadata layer:
- You need to create a custom table in which to store the metadata
- You need to make WordPress aware of the meta table
- You can optionally define wrapper functions or class methods for the core metadata functions that make it easier to interact with your metadata layer
Let’s look at each piece.
Creating the metadata table
The metadata has to be stored somewhere. In the vast majority of cases, the best option will likely be to register a custom table that closely mimics the default metadata tables for posts, users, comments, and terms.
If you’re unfamiliar with creating custom database tables, I’d recommend you read through my series on building a database abstraction layer, especially part 3, which covers creating the tables.
The MySQL syntax that WordPress core uses to create the postmeta table looks like this:
CREATE TABLE `wp_postmeta` ( `meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `post_id` bigint(20) unsigned NOT NULL DEFAULT '0', `meta_key` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `meta_value` longtext COLLATE utf8mb4_unicode_ci, PRIMARY KEY (`meta_id`), KEY `post_id` (`post_id`), KEY `meta_key` (`meta_key`(191)) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Unless you have a specific reason for deviating from this structure, I’d recommend using the same table structure.
Let’s use AffiliateWP’s affiliate meta as an example.
The MySQL for our table looks like this:
CREATE TABLE `wp_affiliate_wp_affiliatemeta` ( `meta_id` bigint(20) NOT NULL AUTO_INCREMENT, `affiliate_id` bigint(20) NOT NULL DEFAULT '0', `meta_key` varchar(255) DEFAULT NULL, `meta_value` longtext, PRIMARY KEY (`meta_id`), KEY `affiliate_id` (`affiliate_id`), KEY `meta_key` (`meta_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We create this table when AffiliateWP is first installed, along with our other custom tables.
The structure is simple and mimics core’s metadata structure:
- meta_id – This is an auto incrementing row that holds the ID of the row
- affiliate_id – This is an integer that is set to the ID of the affiliate the metadata belongs to
- meta_key – This is a string identifier for the value stored
- meta_value – This is the actual value of the metadata stored
Registering the metadata table with WordPress
Once the table has been created, we need to make WordPress aware of it. This is what will permit us to utilize the core metadata API functions, such as update_metadata(), to interact with our data.
I would recommend registering the table on the plugins_loaded hook but it could likely be done during other actions as well.
function pw_register_metadata_table() { global $wpdb; $wpdb->affiliatemeta = $wpdb->prefix . 'affiliate_wp_affiliate_meta'; } add_action( 'plugins_loaded', 'pw_register_metadata_table' );
That’s really all there is to it. You can see how we actually do it in AffiliateWP here.
There is one important notes about registering the table. The value passed to $wpdb most follow an exact naming scheme as it defines the value that needs to be passed to the $object_type parameter of the metadata functions.
The type value is determined by everything before “meta”, so in our example above we used “affiliatemeta”, which makes the value we need to pass to the object type “affiliate”.
If you register the table as $wpdb->customermeta, you would pass “customer” as the object type.
Interacting with the metadata
Now that we have created the table and registered it with WordPress, we can use the metadata API functions in WordPress to read, write, and delete to our metadata table.
Add metadata:
$added = add_metadata( 'affiliate', $affiliate_id, 'some_key', 'The value' );
Update metadata:
$updated = update_metadata( 'affiliate', $affiliate_id, 'some_key', 'The value' );
Get metadata:
$value = get_metadata( 'affiliate', $affiliate_id, 'some_key', $single = true );
Delete meta:
$deleted = delete_metadata( 'affiliate', $affiliate_id, 'some_key' );
If desired, this could be formalized a bit more for your specific use case by defining wrapper functions to the core functions. For example, in AffiliateWP, we register methods in our database class like this:
/** * Retrieve affiliate meta field for a affiliate. * * @param int $affiliate_id Affiliate ID. * @param string $meta_key The meta key to retrieve. * @param bool $single Whether to return a single value. * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true. * * @access public * @since 1.6 */ function get_meta( $affiliate_id = 0, $meta_key = '', $single = false ) { return get_metadata( 'affiliate', $affiliate_id, $meta_key, $single ); }
We also define global functions to make the class methods more accessible:
/** * Retrieve affiliate meta field for a affiliate. * * @param int $affiliate_id Affiliate ID. * @param string $meta_key The meta key to retrieve. * @param bool $single Whether to return a single value. * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true. * * @access public * @since 1.6 */ function affwp_get_affiliate_meta( $affiliate_id = 0, $meta_key = '', $single = false ) { return affiliate_wp()->affiliate_meta->get_meta( $affiliate_id, $meta_key, $single ); }
The metadata API in WordPress is really quite nice and does a lot of the heavy lifting for us. There’s no need to write any MySQL for CRUD actions since it’s all handled for us in the metadata API.
Thanks for writing about the metadata API. It’s useful and I don’t think that many developers knows about it
I love the way that you break it down for us beginners. Makes it easy to understand. My experience is not just old-school, but dinosaur-school, and it helps a lot when I can translate it to Assembler or COBOL. 🙂
Thanks a lot Pippin for the tutorial! I have a question. If we have a custom table in database, is it better to create additional columns to store data or create a custom metadata table and store data there?
It depends on the data you’re storing. Do you have an example?
Great article, I wonder if you would find any use in a library I wrote to piggy back the meta system but for custom table structures.
IE You can do something like
update_post_meta( 1, ‘pt_location’, array(
‘city’ => ‘Jacksonville’,
‘state’ => ‘Florida’,
‘zipcode’ => 32204,
‘lat’ => 30.315243,
‘long’ => -81.685681,
) );
Which fills a table with columns city, state, zipcode etc.
Have been meaning to circle around and clean it up a bit but if anybody is interested: https://github.com/danieliser/WP-Post-Partner-Tables
i have also make a plugin blood user registeration form using user meta. with short code .also send mail .task working
sir i have ,name ,email.location ,mobile etc have fields ,
and i want all record store in default wordpress table
my email is ;gurmeet1330@gmail.com
please give me answer on my mail
for example i am no use
this code , i want all record store in default wordpress table
CREATE TABLE `wp_affiliate_wp_affiliatemeta` (
`meta_id` bigint(20) NOT NULL AUTO_INCREMENT,
`affiliate_id` bigint(20) NOT NULL DEFAULT ‘0’,
`meta_key` varchar(255) DEFAULT NULL,
`meta_value` longtext,
PRIMARY KEY (`meta_id`),
KEY `affiliate_id` (`affiliate_id`),
KEY `meta_key` (`meta_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I can’t understand register table section. How this works? `$wpdb->affiliatemeta = …` .
That line simply registers affiliatemeta as a property on the $wpdb object. The value of the property is the name of the affiliate meta database table.
Hello… just one question… what do you suggest if you want to add some meta information dependent both on user and a post type? e.g. if you want to store some specific user preferences on a specific post. Is there any built in functionality to achieve that goal or do I have to create it through custom plugin tables? Thank you in advance!
You could do that with ether user or post meta, or a custom table would also work.
If you need to store a lot of data, use a custom table. It’s if just a few pieces of information, you could use user or post meta and include the user’s ID in the meta_key, allowing you to identify which user the metadata belongs too.
Very very helpfull post, thanks for sharing.
Thanks Pippin your tutorials always rocks.
Metadata API are really useful when it comes to store and retrieve data.anyway as usual love this article//
See here, https://pippinsplugins.com/
Really creative post…thanks for sharing that kind of information..
mobile application development company in UK
Great Work…your post are really creative and too helpful for us…thanks for sharing..
helpful and it really i have used those codes for my website … thanks
TEST
I am grateful to you for this great content.I am reading your article and its very nice, useful. Thanks for sharing such valuable information web designers sydney visit the site to know more.
wordpress plugins_api() way to get the metadata
hello and good day – many thanks pippin,
I’m currently working on a parser to make a small preview on the newest plugins in wordpress.
at the moment i think i work from a URL given
I’d like to retrieve only the title of the page and https://wordpress.org/plugins/wp-job-manager
https://wordpress.org/plugins/ninja-forms
a little chunk of information (a bit of text)
The project: for a list of meta-data of popular wordpress-plugins
(cf. https://de.wordpress.org/plugins/browse/popular/ and gathering the first 50 URLs – that are
50 plugins which are of interest! The challenge is: i want to fetch meta-data of all the existing plugins.
What i subsequently want to filter out after the fetch is – those plugins that have the newest timestamp
– that are updated (most) recently. It is all aobut acutality…
https://wordpress.org/plugins/wp-job-manager
https://wordpress.org/plugins/ninja-forms
https://wordpress.org/plugins/participants-database ….and so on and so forth.
see the source: https://wordpress.org/plugins/wp-job-manager/ we have the following set of meta-data for each wordpress-plugin:
Version: 1.9.5.12
installations: 10,000+
WordPress Version: 5.0 or higher
Tested up to: 5.4 PHP
Version: 5.6 or higher
Tags 3 Tags: database member sign-up form volunteer
Last updated: 19 hours ago
plugin-ratings
but as i saw today: there we have a api that can be used for this purpose too:
cf: https://developer.wordpress.org/reference/functions/plugins_api/
The API is the proper way to get the metadata. However it is not very fast. For 50 calls it might take 1-2 min.
I have the feeling that WP intentionally makes this call slow to discourage others from mining the plugin information.
so – i am glad to see this reference here
thank you
I am looking for a way that helpscout tickets created with Gravity Form, carry the TAG that Restrict Content Pro membership has acquired.
I’m confused!