Update: For more information, you can download our free ebook, The Ultimate Guide to the WordPress REST API.
In my previous articles on the JSON REST API, I focused primarily on retrieving and editing posts — both from the current site and from a remote site. In this article, I’m going to branch out a bit and focus on post meta. I’ll be covering how to edit and create meta fields using the API, and how to search for them.
Before We Begin
There are a lot of ways to add and edit custom fields — i.e., meta data — in WordPress.
Though not particularly good, WordPress provides a basic custom field UI for editing post meta, which I wrote about here. Alternatively, you can work with fields programmatically using native WordPress functions, which you can read more about here.
If you want to do any serious work with meta fields in WordPress, you really should use a custom fields plugin. For this tutorial, I recommend that you install Pods.
Pods gives you an easy interface for working with custom fields via the WordPress admin. More importantly, it provides an easier way to get and manipulate post meta via the API.
So, to get started, install Pods via the WordPress plugin installer. Then from the Pods editor, create a new Pod — make sure it’s a custom post type Pod and call it “jedi.” From the Pods Editor — which is the next screen you will be taken to — you can add two fields by clicking the blue “Add Field” button.
Label one field “Home Planet” and the other “Lightsaber Color.” Don’t worry about any of the options, the defaults will do. Do notice, however, that Pods automatically sets the field names with lower case letters and underscores, so your fields are called “home_planet” and “lightsaber_color.”
Now from the post editor, create two new posts in the Jedi custom post type. Create one called “Luke Skywalker.” His homeworld is “Tatooine” and his lightsaber color is “green.” Then create a second one called “Leia Organa Solo.” Her homeworld is “Alderaan,” her lightsaber color is “blue,” and yes I’ve read too many Star Wars Expanded Universe novels and no I don’t care that Disney has removed them from the cannon.
Limitations
Presuming that you have the REST API installed, and that you’ve read my last few articles on the API, fetching these posts should be fairly straight forward. If you query for a post, you’ll notice that your meta fields for the posts are not included.
Another limitation is that you can’t query by meta field. In theory — since the API uses WP_Query — you should be able to add meta_key and meta_ value filters, like this:
http://example.com/wp-json/?posts&filter[meta_key]=home_planet&[meta_value]=tatooine
However, that’s not going to work here because the REST API only allows certain arguments to be passed to WP_Query, and by default meta_key and meta_value are not allowed. Though, this is probably a good thing because you don’t want all of your meta fields exposed to the world.
If you really want to do this, and have considered all the drawbacks, it is possible via the json_query_vars filter. This defines which filters can be passed from the API request to WP_Query. We can add meta_key and meta_value to the allowed arguments, like this:
add_filter( 'json_query_vars', 'slug_allow_meta' ); function slug_allow_meta( $valid_vars ) { $valid_vars = array_merge( $valid_vars, array( 'meta_key', 'meta_value' ) ); return $valid_vars; }
Keep in mind that this exposes all meta fields for all posts to public queries.
There is an open issue to allow limited meta queries directly via this method. As with all things open source, if this limitation is an issue, you can either work around it — which I will show you two methods for, in this article — or you can fix it.
This limitation is the real reason why I recommend using Pods for adding and managing custom fields. When you have the Pods JSON API plugin activated, it’s much easier to access and save meta fields via the endpoints, which the plugin adds to the REST API.
Using The Pods Endpoints To Get Meta Fields
Pods’ add-on for the REST API adds an endpoint for each “Pod” at pods/<pod-name>. This means we can get all posts in our custom post type at /pods/jedi. The Pods JSON API uses the find method of the Pods class to build its queries. This is because the Pods class supports custom fields stored in alternative database tables, as well as in the standard post meta table.
Working with the find method of the Pods class is a large topic that’s covered extensively on the Pods documentation, which also includes plenty of examples. For now, we’ll simplify matters by using its search parameter, which will allow us to find a post in our custom post type by its title. We can retrieve our “Luke” post like this:
$params = array( 'search' => 'Luke', ); $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'admin' . ':' . 'password' ), ); $url = json_url( 'pods/jedi' ); $url = add_query_arg( $params, $url ); $response = wp_remote_get( $url, array( 'headers'=> $headers ) ); var_dump( wp_remote_retrieve_body( $response ) );
Now if you look at the response you will see that our meta fields are there for us to see, and output, as needed.
Saving Meta Fields Using the Pods JSON API
Saving meta fields using the Pods JSON API is very simple. The first thing you need to do is create an array of data that you want to update, with the keys equal to the meta key and value equal to the meta value. You then make an authenticated POST request to the same endpoint you used to get the item with that array (encoded as JSON of course) as the body of the request.
Here is an example, updating Luke’s Lightsaber color to red:
$data = array( 'lightsaber_color' => 'red' ); $url = json_url( 'pods/jedi/8' ); $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'username' . ':' . 'password' ), ); $response = wp_remote_post( $url, array ( 'method' => 'POST', 'headers' => $headers, 'body' => json_encode( $data ) ) ); //make sure response isn't an error if ( ! is_wp_error( $response ) ) { //show the updated post item var_dump( wp_remote_retrieve_body( $response ) ); }
We can even create an entirely new item using the same strategy. We can create an item in the jedi post type and populate both the meta fields and the post title field with one request. Here’s how we can add an entry for “Obi-Won Kenobi:”
$data = array( 'title' => 'Obi-Won Kenobi', 'lightsaber_color' = 'blue', 'home_planet' => 'Stewjon', ); $url = json_url( 'pods/jedi' ); $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'username' . ':' . 'password' ), ); $response = wp_remote_post( $url, array ( 'method' => 'POST', 'headers' => $headers, 'body' => json_encode( $data ) ) ); //make sure response isn't an error if ( ! is_wp_error( $response ) ) { //show the updated post item var_dump( wp_remote_retrieve_body( $response ) ); }
Saving Meta Fields Directly With The REST API
If you don’t want to use Pods and the Pods JSON API add-on, you can still manipulate post meta via the REST API directly. However, it’s slightly more complicated. Doing so requires sending a POST request to a special endpoint for the individual meta key. That endpoint is in the form of posts/<post-id>/meta/<meta-id>.
Meta key IDs aren’t something that we usually concern ourselves with, but every meta key/value pair in the database has a unique ID. Typically WordPress takes care of finding that ID for us when we use a function like get_post_meta().
Fortunately, we can get the meta ID when we query by a meta key for a particular post. So if we don’t know the key’s ID, the first step in updating a meta key on a remote site will be to query for all post meta using the posts/<post-id>/meta endpoint.
Here’s how I queried for the meta fields on a remote site with the “jedi” post type. As you can see, I’m using the endpoint “posts/9/meta” becasue 9 is the post ID for my “Leia Organa Solo” post.
$url = 'http://local.wordpress-trunk.dev/wp-json/'; $url .= 'posts/9/meta/'; //This example uses the basic authentication plugin for authentication $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'admin' . ':' . 'password' ), ); $response = wp_remote_post( $url, array ( 'method' => 'POST', 'headers' => $headers, ) ); //make sure response isn't an error if ( ! is_wp_error( $response ) ) { //show the body of the response. print_r( wp_remote_retrieve_body( $response ) ); }
This was the response I got:
[{"ID":145,"key":"home_planet","value":"aleraan"},{"ID":236,"key":"lightsaber_color","value":"Blue"},]
This shows me that the meta ID for the “home_planet’ meta key is 145. Now, if I want to update the value of that field to reflect her planet of birth, “Polis Massa,” I can POST that to the endpoint for this meta ID: posts/9/meta/145.
Doing so would look similar to the last bit of example code, but I’d use POST instead of get for my method, and I’d pass a JSON encoded array of key/value pairs to update the meta in the body of the request, like this:
$url = 'http://local.wordpress-trunk.dev/wp-json/'; $url .= 'posts/9/meta/145'; //setup the data $data = array( 'key' => 'home_planet', 'value' => 'Polis Massa' ); //This example uses the basic authentication plugin for authentication $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'admin' . ':' . 'password' ), ); $response = wp_remote_post( $url, array ( 'method' => 'POST', 'headers' => $headers, 'body' => json_encode( $data ), ) ); //make sure response isn't an error if ( ! is_wp_error( $response ) ) { //show the body of the response. print_r( wp_remote_retrieve_body( $response ) ); }
It’s important to note that there are automated ways to get the meta ID of a meta field. I wrote a function that automates the process of doing the two requests I showed above, which you can find here.
Stay tuned for my next article on the REST API, where I’ll be covering using the REST API to help you work with forms. If you have any other topics on the REST API you’d like to see me cover in the future, please let me know in the comments below!
4 Comments