Migrating Comments using WP REST API

A few weeks ago I finished development on my new website, and it was time to finalize content. I had hoped to have most of it ready before development, but dev is more fun than writing so I had procrastinated.

By the time I was ready to launch, there were about 20 new, approved comments on my blog. Instead of abandoning them or manually re-typing them on the new site, I migrated them with the REST API.

First, I recommend using Insomnia for Mac. It makes testing requests so easy.

Retrieving Comments

You can use the comments endpoint to retrieve a list of latest comments. Since I needed about 20, I set 'per_page' => 20. Example:

However, this doesn’t include the commenter’s email address, which I’ll need. Changing the context to edit provides the fields we need, but we then need to authenticate ourselves.

I’m only pulling the data locally, so the simplest approach for me was to install the Basic Auth plugin which lets me pass my username and password with the request. Obviously you wouldn’t want this in a production environment – you don’t want your username/password sitting in the code.

After adding 'context' => 'edit' to the query, clicking the “Basic” tab and adding my username/password, the response now includes the commenter’s email address.

Now I know how to retrieve the data I need, it’s time to move it to code.

<?php
/**
* Comments Migration
*
*/
function ea_comments_migration() {
$site_url = 'https://yoursite.com';
$username = 'your_username';
$password = 'your_password';
$query_args = array(
'per_page' => 20,
'context' => 'edit',
);
$request_args = array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $username . ':' . $password ),
),
);
$url = add_query_arg( $query_args, trailingslashit( $site_url ) . 'wp-json/wp/v2/comments' );
$response = wp_remote_get( $url, $request_args );
if( '200' != wp_remote_retrieve_response_code( $response ) )
return;
$comments = json_decode( wp_remote_retrieve_body( $response ) );
if( empty( $comments ) )
return;
}
view raw migration.php hosted with ❤ by GitHub

I’ve defined the $site_url, $username, and $password at the top for easy editing. We then put together the query arguments (how many to request and the context), and the request arguments (basic authorization header). We make our request using wp_remote_get().  Only continue if we get 200 (Success) as the response code and there are comments.

Saving Comments

This part is pretty straightforward. We’ll build an array of all the comment data and use the wp_new_comment() function to add it.

We should reverse the list of comments so we start with the oldest. Some of the newer comments may be replies to older ones. We’ll also want to confirm the comment isn’t already in the database by checking get_comment( $comment->ID ).

After inserting a comment, we’ll need to save its comment IDs from the old site and new site (since they will be different) in an array, then refer to that when specifying a comment’s parent.

Here’s the final code:

<?php
/**
* Comments Migration
*
*/
function ea_comments_migration() {
$site_url = 'https://yoursite.com';
$username = 'your_username';
$password = 'your_password';
$query_args = array(
'per_page' => 20,
'context' => 'edit',
);
$request_args = array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $username . ':' . $password ),
),
);
$url = add_query_arg( $query_args, trailingslashit( $site_url ) . 'wp-json/wp/v2/comments' );
$response = wp_remote_get( $url, $request_args );
if( '200' != wp_remote_retrieve_response_code( $response ) )
return;
$comments = json_decode( wp_remote_retrieve_body( $response ) );
if( empty( $comments ) )
return;
// Reverse them so we start with oldest first
$comments = array_reverse( $comments );
// Save Old/New IDs for parent linking
$comment_ids = array();
foreach( $comments as $comment ) {
// Skip if comment exists
$existing = get_comment( $comment->id );
if( !empty( $existing ) && ! is_wp_error( $existing ) )
continue;
$parent = !empty( $comment->parent ) && array_key_exists( $comment->parent, $comment_ids ) ? $comment_ids[ $comment->parent ] : $comment->parent;
$commentdata = array(
'comment_post_ID' => $comment->post,
'comment_author' => $comment->author_name,
'comment_author_email' => $comment->author_email,
'comment_author_url' => $comment->author_url,
'comment_content' => $comment->content->raw,
'comment_type' => '',
'comment_parent' => $parent,
'user_id' => $comment->author
);
$id = wp_new_comment( $commentdata, true );
if( ! is_wp_error( $id ) )
$comment_ids[ $comment->id ] = $id;
}
}
//add_action( 'init', 'ea_comments_migration' );
view raw migration.php hosted with ❤ by GitHub

I placed this code in a migration.php file in Core Functionality, hooked to init and commented out. When I’m ready to migrate, uncomment the hook, load the page (it takes a few seconds while it saves all the comments), then comment it out again.

Issues

If you have a lot of comments to migrate, it will time out while processing. You’ll need to find a way to batch it.

I’d probably store the old comment ID as comment metadata when saving. Before looping through and saving new comments, I’d do a WP_Comment_Query for all previously saved ones (comments with the _old_comment_id meta key) and make the old/new ID array, called $comment_ids in the above code.

Tutorial

Receive New Posts by Email

Leave a comment