Update: For more information, you can download our free ebook, The Ultimate Guide to the WordPress REST API
In last week’s article, I showed just how easy it is to use the new WordPress REST API to get posts from a remote site. In this article we’re going to build on what we learned last week and use the REST API to copy posts from one site to another. First I’ll show you how to create a post in a site with data sourced from a remote site via the REST API. Then I’ll show you how to take a post on a site and create a copy of it on a remote site.
In this article’s example code we will be passing data between two WordPress sites created with VVV. Combined with what you learned in part one of this introduction, by the end of this article you will be able to not only display posts from a remote site, but create posts on a remote site.
By itself that’s pretty cool, but as I said last week, this is foundational work that is designed to make you comfortable passing JSON objects via HTTP requests.
Once you’re comfortable with that, given that JSON is the universal standard for data exchange, you can apply this knowledge to pass any type of data between WordPress and non-WordPress powered apps and sites. That opens up a world of possibility. And the best part is, it’s actually quite simple.
Before We Get Started
I’m going to be passing data between two of the default sites created with VVV. If you’re not familiar with VVV, see my article from a few weeks ago.
The two site URLs are http://local.wordpress.dev and http://local-trunk.wordpress.dev. You can use whatever system you want for learning this work, but I can’t stress enough how important it is to use local sites, not live sites accessible over the internet.
I say this because in this article we’ll use the basic authentication plugin for the REST API. Because it transmits your username and password as base64 encoded plain text, it is not considered secure for the real world. On live servers you should use the oAuth plugin, which I will not be covering in this article as that’s a topic of its own.
Before we begin, make sure you install and activate the REST API plugin and the basic authentication plugin. There is no configuration for either.
Also, in last week’s article, I showed you how to create two functions: One for creating URL strings for generating GET requests via the REST API, and the other for making those requests. Make sure you are able to execute both of those functions as we will be using them in this article.
I posted them as a plugin on GitHub.
You can either download and install the plugin, or copy and paste the functions elsewhere on your site.
Copying Posts from a Remote Site
The first thing I want to show you is how to take a post from one site and create a copy of it on another. This is very simple.
The first step is to get the JSON post data from the REST API and convert it to a PHP object. With what we did last week, that’s actually quite simple.
All we need to do is this:
$url = 'http://local.wordpress-trunk.dev/wp-json/posts/1'; $post = slug_get_json( $url );
Now, with a bit of work, we can turn $post into an array, which we can pass to wp_insert_post() to create a copy of the post.
The REST API returns an array that has slightly different key names than wp_insert_post() is expecting. For example, it has a key called ‘title,’ however we need a key called ‘post_title’ in the array we pass to wp_insert_post().
You can use this function to do the conversion:
function slug_insert_post_from_json( $post ) { //check we have an array or object //either make sure its an array or throw an error if ( is_array( $post ) || is_object( $post ) ) { //ensure $post is an array, converting from object if need be $post = (array) $post; } else { return sprintf( 'The data inputted to %1s must be an object or an array', __FUNCTION__ ); } //set up an array to do most of the conversion in one loop //Note: We set ID as import_id to ATTEMPT to use the same ID //Leaving as ID would UPDATE an existing post of the same ID $convert_keys = array( 'title' => 'post_title', 'content' => 'post_content', 'slug' => 'post_name', 'status' => 'post_status', 'parent' => 'post_parent', 'excerpt' => 'post_excerpt', 'date' => 'post_date', 'type' => 'post_type', 'ID' => 'import_id', ); //copy FROM json array TO how wp_insert_post() wants it and unset old key foreach ( $convert_keys as $from => $to ) { if ( isset( $post[ $from ] ) ) { $post[ $to ] = $post[ $from ]; unset( $post[ $from ] ); } } //prepare author ID $post[ 'post_author' ] = $post[ 'author' ]->ID; unset( $post[ 'author' ] ); //put terms object into it's own array and unset //for now let it be. $terms = (array) $post[ 'terms' ]; unset( $post[ 'terms'] ); //create post and return its ID return wp_insert_post( $post ); }
This function my appear complicated, but we can break it down into a few simple steps.
The first thing it does is check that the data passed into the function is an array or an object. If it is an object, we typecast it as an array. This is important as the rest of the function requires an array.
Once we are sure we have a valid array, we set up a second array, which we will use to simplify converting the array keys. Each value in this array is a key value pair.
For example, ‘excerpt’ => ‘post_excerpt’ will be used to convert the key ‘excerpt’ to ‘post_excerpt.’ We can use that array to copy the value from the old key to a new key and then unset the old key.
One important thing to note is that we converted the key ID to import_id. If we left ‘ID’ as ‘ID,’ WordPress would attempt to update a post of that particular ID. However, the conversion suggests to WordPress that it uses the ID from the remote site as the ID of the new post.
The JSON array returns an object with lots of information about the post author, but we only need their ID, so we can unset the author key after setting the ID from that key to ‘post_author.’ This is actually a tricky step, as it assumes author IDs are the same between sites, which they may or may not be. Depending on your needs, you may have to add additional logic here to ensure that the author names match.
Lastly, we want to unset the ‘terms’ key, which includes information about each taxonomy term for the remote post. If you look in the function you will notice I put that part of the JSON array into the variable $terms. You can expand the function to handle setting and creating taxonomy terms as needed.
In the end, the function passes the prepared array into wp_insert_post() and returns the new post ID. Go ahead and give it a try.
Your First POST Request
So far we’ve used GET requests to get data from a remote site. Now it’s time to use a POST request to create a post on the remote site. This is actually much easier than the last step.
The WordPress HTTP API makes this very easy. Putting together a proper POST request in PHP can be very tricky, but when using wp_remote_post() it’s actually quite simple. This helpful function requires 2 parameters: one is the URL to make the POST request to. The other is an array of arguments to use in the request.
In the code below you’ll see all of the arguments for the POST request. Most are the defaults, shown for completeness, but the two we need to be most concerned with are the body and header. The body is where we put the JSON object to pass to the remote site, and the header is used to pass an authentication array.
Before we look at the complete code, let’s look at how we prepare for the post request by populating two variables, $body and $headers, which we’ll use as the respective body and header of the POST request. We will use the slug_get_json() function from last week’s article to get a JSON array for a post.
We could also get the post with get_post() and then, after some manipulation, convert it to a JSON array like we did in the last step.
I’ll skip that step and use the REST API to get the data exactly the way the REST API likes it.
Here’s the setup code:
$url = 'http://local.wordpress.dev/wp-json/posts/1'; $body = slug_get_json( $url ); if ( is_object( $post ) ) { $body = $post; $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'admin' . ':' . 'password' ), ); $remote_url = 'http://local.wordpress-trunk.dev/wp-json/posts'; }
The first two lines should look familiar from last week. They give us an object from the REST API. Once we have that in $post, we need to check that it’s an object. If it is we’ll move it into $body.
Next we need to set up our headers. This will be an array containing an index called ‘Authorization,’ and in it we will set a string with the username and password. In the example code, I’m using the default VVV admin password. I hope using that simple of a password highlights how insecure this is. As I said, when doing this on live sites you should use oAuth authentication here.
Before we continue, we put the URL for the remote site’s post endpoint into $remote_url. Now we’re ready to make the POST request. Inside the conditional, we check if $post is an object we can send to wp_remote_post(). This is simple, just pass $headers to ‘headers’ and $body to ‘body.’ Of course, you’ll need to make sure $body is a JSON array by passing it through json_encode.
Here’s the complete code, which as you can see includes some error handling at the end:
$url = 'http://local.wordpress.dev/wp-json/posts/4426'; $post = slug_get_json( $url ); if ( is_object( $post ) ) { $remote_url = 'http://local.wordpress-trunk.dev/wp-json/posts'; $headers = array ( 'Authorization' => 'Basic ' . base64_encode( 'admin' . ':' . 'password' ), ); $response = wp_remote_post( $remote_url, array ( 'method' => 'POST', 'timeout' => 45, 'redirection' => 5, 'httpversion' => '1.0', 'blocking' => true, 'headers' => $headers, 'body' => json_encode( $post ), 'cookies' => array () ) ); if ( is_wp_error( $response ) ) { $error_message = $response->get_error_message(); echo sprintf( '<p class="error">Something went wrong: %1s</p>', $error_message ); } else { echo 'Response:<pre>'; print_r( $response ); echo '</pre>'; } } else { $error_message = 'The input data was invalid.'; echo sprintf( '<p class="error">Something went wrong: %1s</p>', $error_message ); }
Run that in your console and you should get back an array from the remote site with the success code 200 in the header. Alternatively, you may get an error telling you what went wrong.
Don’t REST Yet
These two articles merely introduce the JSON REST API. There’s a growing list of tutorials in the REST API’s documentation that I recommend you keep an eye on. Carl Alexander has a tutorial on creating a WordPress PHP client for the users’ end point, which is more advanced than my tutorials have been.
Armed with all of the knowledge from these two articles on the REST API, combined with the other resources out there, it’s time to start playing with the REST API and see what you can do with it.
It’s a new tool and the possibilities are endless. Have fun and share what you create!
1 Comment