Ok, let me take you on a journey.
Your centralized dev environment sounds very unique, but I think we can work with that. I have a similar system set up: we have a "staging" server with a public domain/ip for clients to view, and each developer has a full copy via git on their local machine with a local Apache server. We point our local dev sites to the central staging server database so that content/settings are consistent. We access our local dev sites with a custom local domain like http://example.local
, the staging site is http://example.staging.com
, and the live site will be http://example.com
.
As an aside, we have wp-config.php
in .gitignore
, so we can each customize it on our local copy. Usually we keep the staging and live versions as differently-named files like wp-config-live.php
and symlink it to wp-config.php
on the live server, etc., so that we can also keep config settings in our private git repo.
Now, we want to work on our local copies of the multisite at example.local
and also access its network sites via subfolders (example.local/site2/
) or other custom domains we have aliased in vhost settings (example2.local
) on each computer. Also, the staging site should work without anything more than a git pull
on the server. On top of that, the live site should also work with a simple git pull
without any database edits. How do we achieve this?
Firstly, our wp-config
across all live/staging/dev environments should contain the standard multisite settings:
define( 'WP_ALLOW_MULTISITE', true );
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', false );
define( 'DOMAIN_CURRENT_SITE', 'example.com' );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );
Notice that DOMAIN_CURRENT_SITE
is the live domain! In order for this whole system to work, the domains defined in our database should all be the live domains, and we will handle each staging/dev site with the following, also in wp-config
(only on dev/staging configs, not in live config):
/** Set this to your local tld setup */
define( 'WP_HOME', 'http://example.local');
define( 'WP_SITEURL', 'http://example.local');
/** Include wp-content/sunrise.php */
define( 'SUNRISE', true );
/** This should be the TLD in the database */
define( 'WP_PROD_TLD', '.com' );
/** This should be the tld of your local copy */
define( 'WP_DEV_TLD', '.local');
The WP_PROD_TLD
and WP_DEV_TLD
are our own custom constants that we will use to check if we need to alter the domain (I realize that you are also using subdomains in your setup, but I'd like to describe a slightly more "standard" approach before attempting to fit your specific circumstance). The SUNRISE
constant is built into WP multisite which just says to include /wp-content/sunrise.php
if it exists. Here is our sunrise.php
file:
<?php
/**
* File sunrise.php
*
* This allows us to copy the production multisite database to staging/dev and still use
* it directly without altering domains
*/
/**
* Filter /wp-includes/ms-load.php get_site_by_path to find production domains
**/
function dev_get_site_by_path($_site, $_domain, $_path, $_segments, $_paths) {
global $wpdb, $path;
// Get our actual domain in the database (should be set to production domain)
// The domain coming in should be the request domain
$domain = str_replace( WP_DEV_TLD, WP_PROD_TLD, $_domain);
// Search for a site matching the domain and first path segment
$site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s and path = %s", $domain, $_paths[0] ) );
$current_path = $_paths[0];
if ($site === null) {
// Specifically for the main blog - if a site is not found then load the main blog
$site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s and path = %s", $domain, '/' ) );
$current_path = '/';
}
// Set path to match the first segment
$path = $current_path;
return $site;
}
add_filter('pre_get_site_by_path', 'dev_get_site_by_path', 1, 5);
add_filter('pre_get_network_by_path', 'dev_get_site_by_path', 1, 5);
/**
* Filter the site_url and home options for each site, and
* filter /wp-includes/link-template.php::network_site_url()
* and /wp-includes/link-template.php::network_home_url()
* so that our network site link is correct in the admin menu
*/
function dev_network_url( $_url = '' ) {
return str_replace( WP_PROD_TLD, WP_DEV_TLD, $_url );
}
add_filter( 'network_site_url', 'dev_network_url' );
add_filter( 'network_home_url', 'dev_network_url' );
add_filter( 'option_siteurl', 'dev_network_url' );
add_filter( 'option_home', 'dev_network_url' );
Note that the sunrise.php
file is ONLY INCLUDED VIA WP-CONFIG ON DEV/STAGING, never on the live server! This is filtering the request to get a network "site by path", which just means it looks at the domain and path requested from the client, and matches the domain and path in the wp_blogs
table. We are checking the domain for our live TLD (in this case, .com
) and replacing it with the TLD of the current environment (our local copies are .local
, the staging server is .staging.com
). It's also filtering the WP_HOME
and WP_SITEURL
options per site at the bottom.
And, that's it! The live domains in the database are being translated on the fly for your local/staging site setup! As long as you're using subfolders or you have aliases set up in your local vhost settings, this should work.
Now, for your case of using subdomains, you might have to do a more thorough filtering of the domains instead of just replacing the TLD. Maybe you settle for the "canonical" domains in the database being multisite.dev
and then in your sunrise.php
file just append the local user's subdomain instead of replacing the TLD, and then do replacements in the database for the live domains once you're ready to migrate.
Credit to "laubsterboy" for getting me started towards this solution: https://www.laubsterboy.com/blog/2014/08/running-development-copy-wordpress-multisite-update/