WordPress多站点中如何正确判断指定站点的自定义文章类型是否激活
这个坑我之前踩过!本质原因是:自定义文章类型是在当前站点的init钩子里注册的,但switch_to_blog()只是切换了数据库连接和站点上下文,不会重新跑一遍目标站点的init钩子。这就导致切换站点后,目标站点的自定义post type根本没被注册到当前运行环境里,所以post_type_exists()的判断结果完全跟着初始加载的站点走,自然会出问题。
下面给你几个靠谱的解决办法,按需选就行:
方法1:切换站点后手动调用注册逻辑
如果你的注册代码是全局可访问的(比如在主题functions.php或者插件文件里),可以把注册逻辑封装成独立函数,切换站点后手动调用它,再做判断:
// 先把注册逻辑单独抽出来,方便复用 function register_recipe_post_type() { $args = array( 'public' => true, 'label' => __( 'Recipe', 'textdomain' ), 'menu_icon' => 'dashicons-book', ); // 只在站点14注册 if( get_current_blog_id() === 14 ) { register_post_type( 'recipe', $args ); } } // 原有的init钩子注册保留 add_action( 'init', 'register_recipe_post_type' ); // 封装一个跨站判断的函数 function is_recipe_active_on_blog( $target_blog_id ) { $current_id = get_current_blog_id(); // 当前就是目标站点,直接判断就行 if ( $current_id === $target_blog_id ) { return post_type_exists( 'recipe' ); } // 切换到目标站点 switch_to_blog( $target_blog_id ); // 手动触发注册逻辑 register_recipe_post_type(); $exists = post_type_exists( 'recipe' ); // 切回原站点 restore_current_blog(); return $exists; } // 用的时候直接调用 var_dump(is_recipe_active_on_blog(14)); // 不管当前在哪个站点,结果都准确
这个方法的好处是和你原有的注册逻辑完全对齐,不会出现判断条件不一致的问题;缺点是如果注册逻辑很复杂,手动调用可能会有重复执行的风险,但这里我们已经加了站点ID判断,所以只会在符合条件的站点注册,没问题。
方法2:直接查目标站点的数据库(最靠谱)
WordPress会把已注册的自定义文章类型存在站点的wp_post_types选项里(多站下表前缀是wp_{blog_id}_options),我们直接查这个选项就行,不用走注册流程:
function is_recipe_active_on_blog( $target_blog_id ) { $current_id = get_current_blog_id(); $exists = false; if ( $current_id === $target_blog_id ) { $post_types = get_option( 'wp_post_types' ); $exists = isset( $post_types['recipe'] ); } else { // 获取目标站点的数据库表前缀 $blog_prefix = $GLOBALS['wpdb']->get_blog_prefix( $target_blog_id ); // 直接查目标站点的options表 $post_types_data = $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare( "SELECT option_value FROM {$blog_prefix}options WHERE option_name = %s", 'wp_post_types' ) ); if ( $post_types_data ) { // 反序列化选项值 $post_types = maybe_unserialize( $post_types_data ); $exists = isset( $post_types['recipe'] ); } } return $exists; } // 使用示例 var_dump(is_recipe_active_on_blog(14)); // 结果绝对准确
这个方法是我最推荐的——完全不依赖运行时的钩子或注册逻辑,直接从数据库拿最原始的信息,性能也更好,适合需要频繁跨站判断的场景,比如你做hreflang链接的情况。
方法3:清除post type缓存后用内置函数(不太推荐)
如果你想用WordPress原生的post_type_exists,可以切换站点后清除post type的缓存,再判断:
function is_recipe_active_on_blog( $target_blog_id ) { $current_id = get_current_blog_id(); if ( $current_id === $target_blog_id ) { return post_type_exists( 'recipe' ); } switch_to_blog( $target_blog_id ); // 清除post type的缓存 wp_cache_delete( 'post_types', 'wp' ); $exists = post_type_exists( 'recipe' ); restore_current_blog(); return $exists; }
不过这个方法有点玄学,有时候缓存清不干净,结果就不准,所以除非你特别想用内置函数,否则还是优先选前两种。
总结
做hreflang链接这种需要准确判断的场景,直接用方法2准没错;如果你的注册逻辑有额外的依赖(比如要检查某个插件是否激活才注册),那方法1更合适,能保证判断逻辑和注册逻辑完全一致。
内容的提问来源于stack exchange,提问作者marks

