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.

Bill Erickson

Bill Erickson is the co-founder and lead developer at CultivateWP, a WordPress agency focusing on high performance sites for web publishers.

About Me
Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments are closed. Continue the conversation with me on Twitter: @billerickson

Comments

  1. Hermann LAHAMI says

    I think there should be a better way to do these kind of things. This happened for you for the comments, for others woocommerce orders or contact form submissions. Is it not possible to have a tool to sync any kind of content between 2 sites?

    • Bill Erickson says

      It’s a hard problem to solve. Delicious Brains tried solving it with MergeBot but it proved to be too complex of a problem (see here). When I beta tested MergeBot, it took more time to write the schema for plugins to say what data it’s storing than it took to manually rebuild everything.

      • Hermann LAHAMI says

        I think that approach is good for developers but not for regular users. What if we were simply able to compare two databases and id the differences (of course after leaving out data such as the comments spams or posts revisions if the user wants to exclude them)? If we were able to find out for example that a post A is present on site X and post B is present on site Y using their ids as matching criteria we may be to work something out. I’m not saying that it can be perfect but won’t that approach be ok where mergebot failed?

        Sorry for the late answer, i did not get the notification for the comment publication.