适用于所有开发人员的 WordPress 多站点和单一数据库

WordPress multisite and single database for all developers

我正在为 WordPress 多站点设置规划开发团队工作流程。我们之所以选择多站点,是因为我们想限制 update/upgrade 流程,并且因为就后端而言,我们的绝大多数客户都拥有相同的网站 structure/requirements。对于前端,我们使用 'vanilla' 主题,其中包含我们的 css/js 框架和主要设置、post 类型、选项等,我们将基于以下内容构建我们所有的网站我们的香草主题并通过子主题为每个客户扩展它。 我决定将所有内容都置于版本控制之下 (Git),甚至是 WordPress 核心文件。

就我们当前的开发环境而言:我们一直在使用集中式 Web 服务器,而不是 LAMP 堆栈或 VVV/Docker 设置,每个开发人员都通过 url 通过为每个项目(站点)使用单独的虚拟主机映射到他的本地存储库。因此,在处理项目 A 时,John 的 url 是 http://john.projectA.dev, whereas Jack's url is http://jack.projectA.dev.

为所有开发人员使用相同的数据库最初似乎是 'logical' 的方式,以确保数据一致性、通用设置等,但我还无法确定结果。由于 WordPress 在 wp_options table 中存储站点 url 和主页 url URL 的方式,我未能找到映射 John 的方法通过 URL 访问他将要工作的站点,或者允许所有开发人员访问公共多站点的 wp-admin,默认为用户的 URL 之一(即 http://bob.wpmulisite.dev,其中 Bob最初设置多站点环境的开发人员。 我尝试通过在每个开发人员的自定义 wp-configs 中定义它们来覆盖 url,但这并没有走得太远,因为 WP_HOME 和 WP_SITEURL 在 WP 多站点中被忽略了。

我不喜欢必须使用数据库转储、替换转储中的 urls 并将其导入回每个用户的数据库的想法,因为我担心这个过程很快会导致问题,它会造成太多的合并、差异化等。但是,我可能是错的,我绝对愿意接受任何建议。

我已经在上面概述了我的主要问题,如果您觉得不清楚,我很乐意重新措辞。在这一点上,我不太确定您可能对哪个级别的详细信息感兴趣,所以请问我,我会深入探讨。

好,我带你去旅行

您的集中式开发环境听起来很独特,但我认为我们可以使用它。我有一个类似的系统设置:我们有一个 "staging" 服务器和一个 public domain/ip 供客户查看,每个开发人员通过 git 在他们的本地有一个完整的副本带有本地 Apache 服务器的机器。我们将本地开发站点指向中央登台服务器数据库,以便 content/settings 保持一致。我们使用 http://example.local 等自定义本地域访问我们的本地开发站点,暂存站点为 http://example.staging.com,实时站点为 http://example.com.

顺便说一句,我们在 .gitignore 中有 wp-config.php,所以我们每个人都可以在我们的本地副本上自定义它。通常我们将暂存版本和实时版本保存为 differently-named 文件,如 wp-config-live.php 并将其符号链接到实时服务器上的 wp-config.php 等,这样我们也可以将配置设置保存在我们的私有 git回购。

现在,我们要处理位于 example.local 的多站点的本地副本,并通过子文件夹 (example.local/site2/) 或我们在虚拟主机设置 ( example2.local) 在每台计算机上。此外,暂存站点应该在服务器上只需要 git pull 就可以工作。最重要的是,实时站点应该 可以使用简单的 git pull,无需任何数据库编辑。我们如何实现这一目标?

首先,我们在所有 live/staging/dev 环境中的 wp-config 应包含标准多站点设置:

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 );

注意 DOMAIN_CURRENT_SITElive 域!为了让整个系统正常工作,我们数据库中定义的域都应该是活动域,我们将使用以下内容处理每个 staging/dev 站点,也在 wp-config 中(仅在 dev/staging 配置,不在实时配置中):

/** 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');

WP_PROD_TLDWP_DEV_TLD 是我们自己的自定义常量,我们将使用它们来检查是否需要更改域(我知道您在设置中也使用了子域,但我在尝试适应您的具体情况之前,我想描述一种稍微 "standard" 的方法)。 SUNRISE 常量内置于 WP multisite 中,它只是说如果 /wp-content/sunrise.php 存在则包含它。这是我们的 sunrise.php 文件:

<?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' );

请注意 sunrise.php 文件 仅通过 WP-CONFIG 包含在 DEV/STAGING 中,永远不会在实时服务器上!这是过滤获取网络"site by path"的请求,这只是意味着它查看从客户端请求的域和路径,并匹配wp_blogs table中的域和路径。我们正在检查我们的实时 TLD 的域(在这种情况下,.com)并将其替换为当前环境的 TLD(我们的本地副本是 .local,登台服务器是 .staging.com ).它还在底部过滤每个站点的 WP_HOMEWP_SITEURL 选项。

而且,就是这样!数据库中的实时域正在为您的 local/staging 站点设置即时翻译!只要您使用的是子文件夹,或者您在本地虚拟主机设置中设置了别名,这就应该有效。

现在,对于使用子域的情况,您可能必须对域进行更彻底的过滤,而不是仅仅替换 TLD。也许您满足于数据库中的 "canonical" 域是 multisite.dev,然后在您的 sunrise.php 文件中只附加本地用户的子域而不是替换 TLD,然后在数据库中进行替换准备好迁移后的实时域。

感谢 "laubsterboy" 让我开始使用这个解决方案:https://www.laubsterboy.com/blog/2014/08/running-development-copy-wordpress-multisite-update/

此建议适用于插件开发。不是 "design" 或客户特定主题开发:

如果您要从头开始建立 WordPress 开发商店,我强烈建议您学习如何使用 Codeception 中的 WordPress 模块进行行为和测试驱动开发。它可以从不同的角度[验收、WPunit 测试、功能测试等] 进行各种测试。它可以轻松地在需要整个数据库转储、无转储、URL 替换、空白数据库等的测试之间切换,并且可以根据您的需要进行混合和匹配。

进行 TDD 的主要原则之一是,测试之间的依赖性应该最低。你说的这些共享设置是什么?这些应该尽可能地减少。当您这样做时,您可以将它们一一列出,而不是将您的设置视为某种有机整体。

例如,假设您正在制作一个插件,无论出于何种原因需要特定类型的永久链接结构。一种方法就是您所建议的:即拥有某种共享服务,开发人员可以在其中访问一个环境,在该环境中他们可以获得所需的永久链接结构。有点像所有猪都去吃的东西。

另一种方法是将此需求放入单元测试环境中,然后直接放入 git 存储库中。该需求随 git 中的代码一起分发。然后,在您进行开发时,除了您正在处理的需求之外,您会从绝对不存在的角度来工作。该模块从一个空白的 WordPress 安装开始。在这种情况下,除了我们正在做的事情外,它将完全是空白的,这将是一个特定的永久链接结构。但它可以是任何东西:自定义 post 类型、一组特定的数据、整个数据库、客户端数据等等。

与其尝试在一系列设置中捕获所有这些设置,不如捕获您正在处理的设置并将其置于测试的中心位置。因此,需要这个特定永久链接的插件应该可以在任何其他设置的世界中工作。例如,插件应该适用于任何主题。既然您的要求已内置到测试中,您的程序员在什么样的环境中工作甚至都无关紧要。这使您可以使用远程外包商,并可以灵活地与您的团队合作。如果有人想在笔记本电脑上使用 Eclipse 而有人想在基于云的 PHPstorm IDE 上工作,那都没有关系。他将项目和相关设置与代码一起拉下来。这使您的代码更加稳定,因为它从一开始就设计用于在任何环境中工作。

这里有一个使用这种结构设置插件的教程:https://wordpress-bdd.com/wp-codeception-tutorial/