Tips for Power Users of WP Migrate DB Pro

#
By Jeff Gould
Tips for Power Users of WP Migrate DB Pro

As a developer, I love simple tools that do a specific set of tasks really well and then get out of your way. I use tools like MAMP, WP-CLI, Grunt, and Git as well as simple command-line utilities like z, tail, and rsync every day and they do their jobs well. The only problem is that I’m usually only scratching the surface of what many of these tools can do; I’ve slotted them into my workflow and haven’t really bothered to dig deeper and really discover the potential that they have to offer.

I know I’m biased, but I think WP Migrate DB Pro is also one of those indispensable tools, and for many developers it might just have some untapped potential. So let’s put on our hacker hats and explore beyond the UI into what WP Migrate DB Pro can accomplish when we get creative.

The Life-Changing Magic of Hooking Up

Say what you will about the WordPress codebase, but I think we can all agree that the WordPress hook system is a thing of beauty. WP Migrate DB Pro uses the hook system extensively for both internal use and to enable extensibility and customization. We’ve put together a handy Tweaks plugin which documents most of our available hooks, but I’d like to showcase a couple of specific examples here.

wpmdb_exclude_resized_media

First up is the wpmdb_exclude_resized_media filter which, if you haven’t guessed, will let you forego migrating resized media files when using the Media Files addon. There are a number of reasons you might want to do this. Maybe you’ve been developing a theme and have tried out quite a few different sizes, leaving a lot of cruft in your media folder. Maybe you’re switching to a new theme and plan on regenerating all of the image sizes anyway. Or maybe you just don’t want to use all of that extra bandwidth.

For example, here I’m trying to push a site with just around 100 media files, but with the auto-generated sizes, that’s almost 800 actual files that need to be migrated.

migrating all files

Instead of migrating all of those files, I’ll add the following to an must-use plugin installed onto both sites involved in the migration (since media comparison requires consensus from both sites):

add_filter( 'wpmdb_exclude_resized_media', '__return_true' );

Thanks to the WordPress helper function __return_true, this one-liner is all it takes to return true for the wpmdb_exclude_resized_media filter and since this is in a must-use plugin, I don’t even need to activate it. Running the migration again shows that only the originals are being migrated:

migrating only originals

Now I can just run a thumbnail regeneration plugin on my destination site and I get all of the images for about half the bandwidth! Note that thumbnail regeneration can be a pretty heavy task and you might run into trouble on an underpowered server if you’ve got a lot of images.

wpmdb_migration_complete

I know what you’re saying right now: “You want me to go to my remote and manually click the regenerate media button? Is this the mesolithic age?” I apologize. The sign of a good developer is an extreme penchant for laziness and you clearly can’t be expected to manually initiate media regeneration when you could automate the process. Thankfully we’ve got an action for that!

The wpmdb_migration_complete action will fire on both sites involved in a migration when, you guessed it!, the migration completes. So, continuing on from the previous example (assuming we’ve got WP-CLI installed and your host allows using exec) we can initiate a media regeneration by adding the following to a must-use plugin on the destination site:

add_action( 'wpmdb_migration_complete', 'regen_media_migration_complete', 2, 20 );
function regen_media_migration_complete ( $action, $url ) {
    $wp = "/path/to/wp"; //full path to the WP-CLI command
    global $wp_current_filter;
    $is_remote = strstr($wp_current_filter[0], 'nopriv') ? true : false;
    if ( ( 'push' == $action && $is_remote ) ||
         ( 'pull' == $action && ! $is_remote ) )
    {
        exec( "$wp media regenerate --yes /dev/null 2>/dev/null &" );
    }
}

If everything goes right, this will run WP-CLI’s media regenerate command in a background process (thanks to all that /dev/null nonsense) and if you give it a few minutes, you’ll never even know that the sizes weren’t migrated.

Side note: If you have trouble getting this to work and you’re using something like MAMP, it’s possible that the php exec function is executing in a very different environment from your standard shell. You can overcome this by executing a shell script instead of the WP-CLI command directly. In your shell script you can choose to run the shell that you normally run and even source your .*rc file if that’s where you’ve set the proper php and wp paths. I use zsh, so my shell script would look like this:

#!/bin/zsh
source /Users/me/.zshrc
wp media regenerate --yes --path=/path/to/wordpress

more wpmdb_migration_complete tricks

Utilizing wpmdb_migration_complete to regenerate media is a somewhat specific example, so how about something a bit more generally useful? Sometimes you want to know when a migration has completed without having to watch it run, so let’s see if we can implement a simple desktop notification that will fire off when a migration completes.

We can use the JavaScript Notification API to display notifications and the WordPress Heartbeat API to get the message to the frontend that the migration has completed. If you’re not familiar with the WordPress Heartbeat API, I’d recommend checking out this tutorial from PippinsPlugins.com as that’s where I cribbed most of the heartbeat code from.

First we can use the wpmdb_migration_complete hook to set a transient that says a migration has recently completed:

add_action( 'wpmdb_migration_complete', 'jg_set_complete_transient', 2, 20 );
function jg_set_complete_transient( $action, $url ) {
    global $wp_current_filter;
    $is_remote = strstr($wp_current_filter[0], 'nopriv') ? true : false;
    // only set transient on site running migration
    if ( ! $is_remote ) {
        set_site_transient( 'wpmdb_mig_is_complete', true, 500 );
    }
}

Since the Heartbeat API’s beat throttles itself after a few minutes of inactivity, I’ve set the transient to expire after 500 seconds, which should be plenty long enough for it to get picked up by even the longest tick, but short enough that it expires between migrations.

Next we’ll need to make sure that Heartbeat is running and enqueue our script in the footer:

function jg_heartbeat_enqueue( $hook ) {
    // Make sure the JS part of the Heartbeat API is loaded.
    wp_enqueue_script( 'heartbeat' );
    add_action( 'admin_print_footer_scripts', 'jg_heartbeat_js', 20 );
}
add_action( 'admin_enqueue_scripts', 'jg_heartbeat_enqueue' );

Then we’ll add our script to the footer if users are on the WP Migrate DB Pro page:

function jg_heartbeat_js() {
    // only run heartbeats on WP Migrate DB Pro page
    if ( ! isset( $_GET['page'] ) || $_GET['page'] != 'wp-migrate-db-pro' ) {
        return;
    }
?>
<script>
    (function($){
        //code will go here
    }(jQuery));
</script>
<?php
}

Next, we’ll add some code to that block to make sure that the browser supports the Notifications API and request permission if it does:

// Don't continue if browser not capable
if ( ! ("Notification" in window) ) {
    return;
}
// Get permission if not already granted
if ( 'granted' !== Notification.permission ) {
    Notification.requestPermission();
}

After that we’ll let the Heartbeat API know that we’re listening, this will allow us to run some code on the backend that will check if the wpmdb_mig_is_complete transient has been set only when we’re actually listening:

// Hook into the heartbeat-send
$(document).on('heartbeat-send', function(e, data) {
    data['wpmdb_mig_complete_heartbeat'] = 'check';
});

And then we’ll start listening for our Heartbeat response and fire off a notification if we get it:

// Listen for the custom event "heartbeat-tick" on $(document).
$(document).on( 'heartbeat-tick', function(e, data) {
    //return if flag is not set
    if ( ! data['wpmdb_mig_complete_heartbeat'] ) {
        return;
    }

    //fire notification
    if ( 'granted' === Notification.permission ) {
        var notification = new Notification( "WP Migrate DB Pro Migration complete!", { body: 'Your migration has completed!', icon: 'https://example.com/logo.png' } );
    }
});

Now that we’re done with the JS, we just need to add the code to check if the migration has completed and respond to our own heartbeat requests if it has:

add_filter( 'heartbeat_received', 'jg_heartbeat_received', 10, 2 );
function jg_heartbeat_received( $response, $data ) {
    if ( 'check' == $data['wpmdb_mig_complete_heartbeat'] ) {
        if ( get_site_transient( 'wpmdb_mig_is_complete' ) ) {
            $response['wpmdb_mig_complete_heartbeat'] = "complete";
            // delete transient to ensure that we only send this once
            delete_site_transient( 'wpmdb_mig_is_complete' );
        }
    }
    return $response;
}

Now we’ll get desktop notifications after a migration has completed!

Notifications you actually want to see!

In reality, the notification can take a couple of minutes after a migration has actually completed since the Heartbeat API relies on the frontend polling the backend for updates, but it’s still quite helpful if you’ve tabbed away from the migration screen and need a reminder to get back to work!

Here’s the full source code for the plugin for you to modify or install as-is:

The Reverse Fix

Sometimes the way a server is set up can prevent migrations from working well – either security settings get tripped up by our requests, or settings that control the allowable rate or size of requests aren’t playing nice with the way WP Migrate DB Pro transfers data for push requests. I won’t bore you with the reasons that push can be a bit harder to accomplish than pull, but it often makes things easier if you can just reverse your migration to pull into your destination rather than push into it. If both of your sites are publicly accessible, then this is an easy switch to make. But if you’re trying to migrate to a live site from a local development site, then pulling isn’t an option… or is it?

Enter ngrok – a little command/service combo that lets you create secure tunnels to your locally hosted sites quickly and easily. Generally, ngrok is a bit of a pain to get working with WordPress since it doesn’t do URL or header rewriting (like Apache’s proxy module), so you need to use relative urls to get anything to work. This isn’t an issue for WP Migrate DB Pro migrations since we only need to access the direct URL to your site’s admin-ajax.php file and WP Migrate DB Pro gets a remote site’s URL information from the site’s database rather than just inferring it from the connection URL.

Let’s get started! First, you’ll want to download ngrok and open your CLI to wherever you downloaded the zip. Next you’ll want to unzip it, give it execution permissions, and move it into your /usr/bin folder so you can run it from anywhere without having to specify the whole path:

$ cd /Users/me/Downloads
$ unzip ngrok-stable-darwin-amd64.zip #filename may vary
$ chmod +x ngrok
$ mv ngrok /usr/bin

If everything went according to plan, you should be able to run $ ngrok -v and receive output similar to ngrok version 2.1.3.

Now, I’d like to migrate content from a local site running at deliciousbrains.dev to a live staging site. First, I’ll start ngrok, utilizing the -host-header=rewrite option to specify the virtual host that I want to tunnel to:

$ ngrok http -host-header=rewrite deliciousbrains.dev:80

That should result in your terminal showing a screen like this:

ngrok running

Now, I can grab the WP Migrate DB Pro connection information from deliciousbrains.dev:

https://deliciousbrains.dev/wp
50VdUtDuERaWuJaoeksspelt+6Mr9S4Ir/qfRlm 

And I’ll just replace the hostname with the hostname that ngrok gave me, making sure to preserve the URL path:

https://fc4fba3c.ngrok.io/wp
50VdUtDuERaWuJaoeksspelt+6Mr9S4Ir/qfRlm 

Using this new connection information, I can connect to my local site from any server that has access to the internet, regardless of whether or not it has access to my network.

connecting via ngrok

Anybody who’s used our Multisite Tools addon to migrate subsites to single sites and vice-versa may be interested in this workaround since subsite migrations can currently only be run from the Multisite. This means that you can push a subsite to a single site, or pull a single site into a subsite, but you can’t yet do it the other way around. This can pose a problem if you want to get a local single site install into a live multisite install.

The current workaround is to set up a live single site that you can push your local to and then pull that site into the multisite. It’s a bit of a pain but it does the trick until we get a chance to build out the rest of the features. With ngrok, we can eliminate that intermediary site and pull local single sites into subsites on live multisite installs or push live subsites into our local single sites for development!

A note about this method: In my tests ngrok seems to add a bit of cruft data to responses that is handled correctly by WordPress’ curl http transport but not by the streams transport. I haven’t found a workaround yet, so if you try this out and you’re getting unserialization errors, it’s probably because you’re using streams.

Hack The Planet

I’ll admit, these tips probably aren’t for everybody. If they were, then we’d probably be working to build them into WP Migrate DB Pro. But if you’re willing to get your hands dirty and try some out of the box thinking, you might just find that your favorite daily-use tools can do even more than you previously thought.

What are your favorite pro tips for WP Migrate DB Pro or any of your essential workflow tools?

About the Author

Jeff Gould

Jeff is a problem solver at heart who found his way to the web at an early age and never looked back. Before Delicious Brains, Jeff was a freelance web developer specializing in WordPress and front-end development.