WordPress开发笔记

WordPress标签分类关联自定义文章类型,并显示所挂载标签分类法的文章类型的文章

这个问题确实很常见,你遇到的情况是:虽然从内容管理的角度看,标签已经关联了自定义文章类型,但在前台显示时,WordPress的默认查询逻辑并没有自动包含它们。

问题根源:默认查询的限制

WordPress的标签存档页默认只查询系统默认的“文章”类型。当你把标签挂载到自定义文章类型后,两者在后台建立了关联,但标签存档页面的查询机制并未自动更新以包含这些新的内容类型。

解决思路

最直接有效的解决方案是使用 pre_get_posts 钩子函数,在WordPress执行主查询之前,修改其查询参数,让它同时获取默认文章和你的自定义文章类型。

方法一:自动检测所有支持标签的文章类型

/**
 * 自动获取所有支持标签分类法的文章类型,并在标签存档页显示它们
 */
function auto_add_all_tagged_post_types_to_archive($query) {
    // 确保在前端的主标签存档页面,且不是管理后台
    if (is_tag() && $query->is_main_query() && !is_admin()) {

        // 获取所有公共的文章类型
        $all_post_types = get_post_types(array('public' => true));

        // 过滤出支持标签分类法的文章类型
        $tagged_post_types = array();
        foreach ($all_post_types as $post_type) {
            if (is_object_in_taxonomy($post_type, 'post_tag')) {
                $tagged_post_types[] = $post_type;
            }
        }

        // 如果找到了支持标签的文章类型,就设置查询参数
        if (!empty($tagged_post_types)) {
            $query->set('post_type', $tagged_post_types);
        }
    }
}
add_action('pre_get_posts', 'auto_add_all_tagged_post_types_to_archive');

方法二:更灵活的解决方案(推荐)

/**
 * 更灵活的解决方案:自动包含所有支持标签的文章类型
 * 并提供过滤器供进一步定制
 */
function smart_tag_archive_query($query) {
    // 只在主查询的标签存档页面使用
    if (is_tag() && $query->is_main_query() && !is_admin()) {

        // 使用缓存提高性能
        $tagged_post_types = get_transient('tagged_post_types');

        if (false === $tagged_post_types) {
            // 获取所有公共文章类型
            $all_post_types = get_post_types(array('public' => true));
            $tagged_post_types = array();

            foreach ($all_post_types as $post_type) {
                // 检查该文章类型是否支持标签分类法
                if (is_object_in_taxonomy($post_type, 'post_tag')) {
                    $tagged_post_types[] = $post_type;
                }
            }

            // 缓存结果12小时
            set_transient('tagged_post_types', $tagged_post_types, 12 * HOUR_IN_SECONDS);
        }

        // 允许开发者通过过滤器修改要包含的文章类型
        $final_post_types = apply_filters('tag_archive_post_types', $tagged_post_types);

        if (!empty($final_post_types)) {
            $query->set('post_type', $final_post_types);
        }
    }
}
add_action('pre_get_posts', 'smart_tag_archive_query');

/**
 * 当新的文章类型注册时清除缓存
 */
function clear_tagged_post_types_cache() {
    delete_transient('tagged_post_types');
}
add_action('registered_post_type', 'clear_tagged_post_types_cache');

上述代码缓存结果12小时后,该实现功能还会有效吗?

缓存机制的工作流程

在方法二中,缓存机制是这样工作的:

// 使用缓存提高性能
$tagged_post_types = get_transient('tagged_post_types');

if (false === $tagged_post_types) {
    // 缓存不存在或已过期,重新计算
    $all_post_types = get_post_types(array('public' => true));
    $tagged_post_types = array();

    foreach ($all_post_types as $post_type) {
        if (is_object_in_taxonomy($post_type, 'post_tag')) {
            $tagged_post_types[] = $post_type;
        }
    }

    // 缓存结果12小时
    set_transient('tagged_post_types', $tagged_post_types, 12 * HOUR_IN_SECONDS);
}

12小时后会发生什么

是的,12小时后功能仍然有效! 原因如下:

  1. 缓存自动过期:12小时后,Transient 缓存会自动过期
  2. 自动重新计算:当缓存过期后,get_transient() 会返回 false,触发重新计算逻辑
  3. 获取最新数据:重新计算时会获取当前所有支持标签的文章类型,包括新注册的类型

主动缓存清理机制

更重要的是,方法二包含了主动缓存清理

/**
 * 当新的文章类型注册时清除缓存
 */
function clear_tagged_post_types_cache() {
    delete_transient('tagged_post_types');
}
add_action('registered_post_type', 'clear_tagged_post_types_cache');

这意味着:

  • 无需等待12小时:当有新的自定义文章类型注册时,缓存会立即被清除
  • 即时更新:下次访问标签页面时就会重新计算并包含新的文章类型

改进建议:双重保障

如果你想要更加保险,可以添加额外的缓存清理钩子:

/**
 * 增强的缓存清理机制
 */
function comprehensive_cache_clear() {
    delete_transient('tagged_post_types');
}

// 在多种情况下清理缓存
add_action('registered_post_type', 'comprehensive_cache_clear'); // 新文章类型注册
add_action('registered_taxonomy', 'comprehensive_cache_clear');  // 新分类法注册
add_action('after_switch_theme', 'comprehensive_cache_clear');   // 切换主题时
add_action('activated_plugin', 'comprehensive_cache_clear');     // 激活插件时
add_action('deactivated_plugin', 'comprehensive_cache_clear');   // 停用插件时

方法二的缓存设计是安全有效的

  • 12小时后:缓存自动过期,功能正常
  • 新文章类型注册时:缓存立即清除,即时生效
  • 性能优化:避免每次页面加载都重新计算
  • 未来兼容:自动适应系统变化

这个设计在性能和实时性之间取得了很好的平衡,既避免了频繁的数据库查询,又能及时响应系统变化。

方法三:带排除功能的增强版本

/**
 * 增强版本:自动包含所有支持标签的文章类型,并可排除特定类型
 */
function enhanced_tag_archive_solution($query) {
    if (is_tag() && $query->is_main_query() && !is_admin()) {

        $tagged_post_types = get_transient('enhanced_tagged_post_types');

        if (false === $tagged_post_types) {
            $all_post_types = get_post_types(array('public' => true));
            $tagged_post_types = array();

            foreach ($all_post_types as $post_type) {
                $taxonomies = get_object_taxonomies($post_type);
                if (in_array('post_tag', $taxonomies)) {
                    $tagged_post_types[] = $post_type;
                }
            }

            set_transient('enhanced_tagged_post_types', $tagged_post_types, 12 * HOUR_IN_SECONDS);
        }

        // 默认排除的文章类型(可根据需要修改)
        $default_excluded = array();

        // 允许通过过滤器修改排除列表
        $excluded_types = apply_filters('tag_archive_excluded_post_types', $default_excluded);

        // 从最终列表中移除被排除的类型
        $final_types = array_diff($tagged_post_types, $excluded_types);

        if (!empty($final_types)) {
            $query->set('post_type', $final_types);

            // 可选:设置每页显示数量
            $posts_per_page = apply_filters('tag_archive_posts_per_page', get_option('posts_per_page'));
            $query->set('posts_per_page', $posts_per_page);
        }
    }
}
add_action('pre_get_posts', 'enhanced_tag_archive_solution');

/**
 * 注册新文章类型时清除缓存
 */
function clear_enhanced_tag_cache() {
    delete_transient('enhanced_tagged_post_types');
}
add_action('registered_post_type', 'clear_enhanced_tag_cache');

使用示例和自定义方法

排除特定文章类型

// 在主题的functions.php中添加以下代码来排除特定文章类型
add_filter('tag_archive_excluded_post_types', function($excluded) {
    // 排除'attachment'和'page'类型
    $excluded[] = 'attachment';
    $excluded[] = 'page';
    return $excluded;
});

手动强制包含某些文章类型

// 强制包含某些文章类型,即使它们不支持标签
add_filter('tag_archive_post_types', function($post_types) {
    if (!in_array('my_custom_type', $post_types)) {
        $post_types[] = 'my_custom_type';
    }
    return $post_types;
});

方案特点

  1. 完全自动化:无需手动指定文章类型
  2. 未来兼容:自动适应新注册的自定义文章类型
  3. 性能优化:使用缓存减少数据库查询
  4. 高度可定制:提供过滤器供开发者定制
  5. 智能清理:注册新文章类型时自动清理缓存

方案四:硬编码方案

将下面的代码添加到当前主题的 functions.php 文件中:

/**
 * 让标签存档页面显示自定义文章类型的文章
 */
function add_custom_post_types_to_tag_archive( $query ) {
    // 确保在前端的主标签存档页面,且不是管理后台
    if ( is_tag() && $query->is_main_query() && ! is_admin() ) {
        // 指定要显示的文章类型
        $post_types = array( 'post', '你的自定义文章类型' ); 
        $query->set( 'post_type', $post_types );
    }
}
add_action( 'pre_get_posts', 'add_custom_post_types_to_tag_archive' );

使用说明:

  • 将代码中的 '你的自定义文章类型' 替换为你实际使用的自定义文章类型名称。
  • 如果你的自定义文章类型有多个,可以继续添加到 $post_types 数组中,例如:array( 'post', 'product', 'portfolio' )

推荐使用方法二

推荐使用方法二,它在功能完整性和性能之间取得了很好的平衡,并且提供了足够的灵活性供未来扩展。

这个解决方案将确保你的标签存档页面始终显示所有支持标签分类法的文章类型,无论是现在已有的还是将来新注册的。

补充排查步骤

如果上述方法仍不生效,建议按以下步骤排查:

  1. 确认分类法关联:检查注册自定义文章类型或分类法的代码,确保标签(post_tag)已正确关联到你的自定义文章类型。有时会使用 register_taxonomy_for_object_type() 函数来建立这种关联。
  2. 检查文章类型支持:确认你的自定义文章类型在注册时,在 taxonomies 参数中包含了 'post_tag'(文章标签)。
  3. 清除缓存:确保你清除了所有可能存在的WordPress缓存、浏览器缓存或CDN缓存,以便看到更改后的效果。
  4. 刷新重写规则:在WordPress后台的 设置 > 固定链接 页面,直接点击“保存更改”即可。这会刷新WordPress的重写规则,有时是解决问题的关键一步。

总结

通过 pre_get_posts 钩子修改主查询是解决这个问题的核心方法。如果问题依旧,再按照排查步骤逐一检查。