构建支持多站点的插件和主题

在为 WordPress 多站点网络开发主题或插件时,需要考虑一些与为单站点 WordPress 安装开发略有不同的事项。

在本课中,你将了解一些需要考虑的差异,以及如何确保你的插件和主题支持多站点。

开发主题和子主题

通常,主题和子主题在多站点网络上的工作方式与在单站点上完全相同。一旦主题或子主题被网络激活,它就可以在网络上的任何单个站点上激活。

你可能想要编码到 functions.php 文件中的所有特定功能都将在当前主题的范围内工作。例如,如果你想要在主题的页脚中显示站点名称,你可以使用标准的 get_bloginfo 函数来检索站点名称。

if ( ! function_exists( 'tt3c_get_site_name' ) ) {
    function tt3c_get_site_name() {
        return get_bloginfo( 'name' );
    }
}

然而,假设你想要在主题的页脚中包含主站点的名称,无论当前正在查看哪个站点。你可以使用 switch_to_blog 函数切换到主站点,检索站点名称,然后恢复当前站点。

if ( ! function_exists( 'tt3c_get_site_name' ) ) {
    function tt3c_get_site_name() {
        $site_name = get_bloginfo( 'name' );
        switch_to_blog( 1 );
        $main_site_name = get_bloginfo( 'name' );
        restore_current_blog();

        return $site_name . ' (part of the ' . $main_site_name . ' network)';
    }
}

更进一步,也许你想要仅将主站点排除在此自定义功能之外。你可以使用 is_main_site 函数来检查当前站点是否为主站点,如果是,则直接返回站点名称。

if ( ! function_exists( 'tt3c_get_site_name' ) ) {
    function tt3c_get_site_name() {
        if ( is_main_site() ) {
            return get_bloginfo( 'name' );
        }

        $current_site = get_bloginfo( 'name' );
        switch_to_blog( 1 );
        $main_site_name = get_bloginfo( 'name' );
        restore_current_blog();
        return $current_site . ' (part of the ' . $main_site_name . ' network)';
    }
}

所有这些都可以通过单个子主题中的一个 functions.php 文件实现。

开发插件

如前所述,大多数插件功能在单站点和多站点上的工作方式相同。像 register_post_typeget_posts 这样的函数将以相同的方式运行,只是在特定站点的范围内。

然而,在为多站点开发插件时,需要考虑两件事。

插件通常有一个设置页面,通常可以从管理仪表板访问。这对于单站点插件来说没问题,但在多站点网络上,你需要考虑设置页面应该位于何处。它应该位于网络管理仪表板上,还是位于单个站点仪表板上?

如果你需要在网络管理仪表板上有一个设置页面,你可以使用 network_admin_menu 钩子向网络管理仪表板添加一个菜单项。如果你需要在单个站点仪表板上有一个设置页面,你可以使用 admin_menu 钩子向单个站点仪表板添加一个菜单项。

插件可能必须添加自定义表来存储自定义数据。如果你使用像 $wpdb->prefix 这样的变量来为你的表名添加前缀,你最终会得到一个以站点 ID 为前缀的表名。因此,如果你需要为每个站点提供此功能的自定义表,你需要提前规划。

让我们看一个例子。

这里我们有来自《安全开发插件入门》教程的示例插件。它有一个在插件激活时创建的 form_submissions 表,用于存储表单提交数据。如果你查看代码,你会看到表名以全局 $wpdb 对象的前缀属性为前缀。

在单站点安装中,这意味着它将使用 wp-config.php 文件中定义的前缀创建一个表,在此示例中为 wp_form_submissions

然而,在多站点网络上,根据插件的安装方式,它将创建不同的表。

如果它在网络上的单个站点上激活,表前缀将包含站点 ID。

因此,对于站点 2,表名将是 wp_2_form_submissions

然而,如果插件是网络激活的,激活过程将在主站点的范围内运行,并且它会创建与在单站点安装上激活时相同的表。

因此,对于网络激活,表名将是 wp_form_submissions

问题出现在你查看存储表单提交数据的代码时。

因为这也使用了全局 $wpdb 对象中的相同前缀属性,当此代码在主站点的范围内运行时,它将查找 wp_form_submissions 表来存储数据,但是,例如,当它在网络上的站点 2 的范围内运行时,它将查找 wp_2_form_submissions 来存储数据,而该表并不存在。

为了解决这个问题,我们需要更新插件激活例程,以考虑到这一点:

首先,将表创建代码移动到一个单独的函数中,然后从激活钩子调用此函数。

function wp_learn_create_table(){
    global $wpdb;
    $table_name = $wpdb->prefix . 'form_submissions';

    $sql = "CREATE TABLE $table_name (
      id mediumint(9) NOT NULL AUTO_INCREMENT,
      name varchar (100) NOT NULL,
      email varchar (100) NOT NULL,
      PRIMARY KEY  (id)
    )";

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
}

在 register_activation_hook 钩子的文档中,你会注意到回调函数接受一个 $network_wide 参数。这是一个布尔值,传递给激活钩子回调,指示插件是否正在网络范围内激活。

然后,你可以更新回调函数,首先检查站点是否为多站点网络,以及插件是否正在网络范围内激活。

如果是,则获取并遍历网络中的所有站点,依次切换到每个站点,并为每个站点创建数据表。

或者,如果插件未在网络范围内激活,则只需为当前站点创建数据表即可。

register_activation_hook( __FILE__, 'wp_learn_setup_table' );
function wp_learn_setup_table( $network_wide ) {
    if ( is_multisite() && $network_wide ) {
        $sites = get_sites();
        foreach ( $sites as $site ) {
            switch_to_blog( $site->blog_id );
            wp_learn_create_table();
            restore_current_blog();
        }
    } else {
        wp_learn_create_table();
    }
}

通过在网络上激活插件来测试此功能,您应该会看到所有正确的数据表都已创建。

但是,当创建新站点时会发生什么?在这种情况下,您需要使用像 wp_initialize_site 这样的钩子,为新站点创建数据表。

add_action( 'wp_initialize_site', 'wp_learn_setup_newsite_table' );
function wp_learn_setup_newsite_table( $site ) {
    switch_to_blog( $site->id );
    wp_learn_create_table();
    restore_current_blog();
}

通过创建一个新站点来测试这一点。它应该会在数据库中为该站点的表单提交创建新的数据表。

通过这种方式,您可以让插件在多站点网络上正常工作,无论是在网络上首次激活时(考虑到所有现有站点),还是为将来可能创建的新站点做好准备。

在哪里获取更多信息

除了关于创建网络以及创建网络前需考虑事项的文档外,针对多站点开发的开发者专用文档并不多。

不过,您可以通过浏览 WordPress 代码参考中的多站点包部分,查看所有与多站点相关的功能列表。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注