HUSKY - Products Filter Professional for WooCommerce

如何修复 woocommerce 店铺的分类层级过滤(规范化数据库)

如果您遇到按父类别过滤但未返回任何结果的问题,尽管商品已分配给子类别,本指南将帮助您解决此问题。

问题

Symptom: 当您按父类别进行筛选时,即使其子类别中存在产品,也不会显示任何产品。

示例:

  • 分类结构: Federal Actions > SEC > Enforcement
  • 产品分配给: Enforcement (child category only)
  • 按…筛选 Federal ActionsSEC 返回: 无结果
  • 按…筛选 Enforcement 返回: 产品显示

为什么会这样

WordPress/WooCommerce 需要 products to be explicitly marked with 所有父类别 在层次结构中,而不仅仅是子类别。

When you assign a product to a child category through the WordPress admin interface, WooCommerce doesn’t automatically mark the product with all parent categories in the term relationships table. This is a data structure requirement, not a HUSKY limitation.

正确的分类分配应包括:

  • 子分类: Enforcement
  • 父分类: SEC
  • 祖类别: Federal Actions

如何检查您是否遇到此问题

  1. 转到产品编辑页面: wp-admin/post.php?post=YOUR_PRODUCT_ID&action=edit
  2. 查看右侧的“分类”元框
  3. Check if 所有父类别 在层级结构中被选中
  4. 如果只勾选了子类别 → 您有这个问题

解决方案:自动类别层次结构规范化

This solution consists of two parts:

  1. 面向未来的修复: 保存产品时自动标记父分类
  2. 立即修复: 规范化所有现有产品

第 1 部分:自动标记父分类(未来产品)

将此代码添加到您的子主题的 functions.php:

/**
 * Automatically mark all parent categories when product is saved
 * This ensures hierarchy filtering works correctly
 */
add_action('save_post_product', 'auto_mark_parent_categories', 10, 1);

function auto_mark_parent_categories($product_id) {
    // Clear cache
    clean_object_term_cache($product_id, 'product_cat');

    // Get currently assigned categories
    $terms = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']);

    if (!is_wp_error($terms) AND !empty($terms)) {
        $all_terms = [];

        // For each assigned category, get all its parents
        foreach ($terms as $term_id) {
            $all_terms[] = $term_id;

            // Get all ancestor categories
            $ancestors = get_ancestors($term_id, 'product_cat', 'taxonomy');
            if (!empty($ancestors)) {
                $all_terms = array_merge($all_terms, $ancestors);
            }
        }

        // Assign product to all categories including parents
        wp_set_object_terms($product_id, array_unique($all_terms), 'product_cat');

        // Clear caches
        clean_post_cache($product_id);
        clean_object_term_cache($product_id, 'product_cat');

        if (function_exists('wc_delete_product_transients')) {
            wc_delete_product_transients($product_id);
        }
    }
}

其功能:

  • 每次保存产品时触发
  • 获取所有分配的分类
  • 查找每个分配类别的所有父类别
  • Marks the product with the complete hierarchy
  • Clears all relevant caches

结果: 从现在开始,任何保存的产品都将自动包含所有父类别。

第二部分:规范化现有产品(一次性修复)

此脚本将修复您数据库中的所有现有产品。添加此 临时的 代码到您的 functions.php:

/**
 * One-time script to normalize all existing products
 * Access: yourdomain.com/?normalize_cats=1 (must be logged in as admin)
 */
add_action('init', function () {
    if (isset($_GET['normalize_cats']) AND current_user_can('manage_options')) {
        $result = normalize_all_products_categories();
        echo '<h2>Category Normalization Results</h2>';
        echo '<pre>';
        print_r($result);
        echo '</pre>';
        die();
    }
});

function normalize_all_products_categories() {
    global $wpdb;

    // Get all product IDs
    $args = [
        'post_type' => 'product',
        'post_status' => 'any',
        'posts_per_page' => -1,
        'fields' => 'ids'
    ];

    $product_ids = get_posts($args);
    $log = [];
    $normalized_count = 0;

    foreach ($product_ids as $product_id) {
        // Get current categories
        $terms = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']);

        if (!is_wp_error($terms) AND !empty($terms)) {
            $all_terms = [];

            // Collect all parent categories
            foreach ($terms as $term_id) {
                $all_terms[] = $term_id;

                // Get ancestors
                $ancestors = get_ancestors($term_id, 'product_cat', 'taxonomy');

                if (!empty($ancestors)) {
                    $all_terms = array_merge($all_terms, $ancestors);
                }
            }

            $all_terms = array_unique($all_terms);

            // Only update if we're adding new parent categories
            if (count($all_terms) > count($terms)) {
                wp_set_object_terms($product_id, $all_terms, 'product_cat');
                $normalized_count++;
                $log[] = "Product #{$product_id}: added " . (count($all_terms) - count($terms)) . " parent categories";
            }
        }
    }

    // Log to WordPress error log
    error_log("=== Category Normalization Complete ===");
    error_log(implode("\n", $log));
    error_log("Total normalized: {$normalized_count} products");

    return [
        'success' => true,
        'normalized' => $normalized_count,
        'total' => count($product_ids),
        'details' => $log
    ];
}

如何使用:

  1. Add the code to your functions.php
  2. 前往: https://yoursite.com/?normalize_cats=1 (必须以管理员身份登录)
  3. 等待脚本完成
  4. 您将看到显示已更新多少产品的结果
  5. 移除此代码 运行一次后

预期输出:

Category Normalization Results
Array
(
    [success] => 1
    [normalized] => 156
    [total] => 919
)

This means 156 products out of 919 were missing parent categories and have been fixed.

测试修复

实施两个部分后:

  1. 前往您的带有 HUSKY 过滤器的商店页面
  2. 尝试按 父类别
  3. 子类别中的产品现在应该会出现
  4. 尝试通过层级深入查看 – 所有层级都应该可以工作

为什么不在 HUSKY 中将其设为默认值?

This is a legitimate question. Here’s why:

  1. 数据结构更改:这会修改核心 WooCommerce 数据关系
  2. 非通用: 有些网站出于特定的业务逻辑原因,有意只分配子分类
  3. 性能: On very large catalogs, this adds overhead to every product save
  4. 冲突: May interfere with other plugins that manage category relationships

未来增强: 我们可以将其添加为 可选设置 在 HUSKY 中,用户可以根据需要启用它,但它不会是默认行为。

替代方案:手动分类分配

如果您偏好手动控制,您可以:

  1. 编辑每个产品
  2. 在“分类”元框中,勾选 所有相关的父类别 手动
  3. 保存产品

这为您提供了精细的控制,但对于大型目录来说非常耗时。

故障排除

脚本显示 0 个标准化产品

  • 您的产品可能已经正确分配了父类别
  • 手动检查几个产品以验证

规范化后筛选仍无法工作

  • 清除所有缓存(WordPress、服务器、浏览器)
  • 确保 HUSKY 使用正确的分类法 (product_cat)
  • 检查与其他过滤插件的冲突
  • 验证产品是否已发布(不是草稿)

子类别过滤器中产品消失

这不应该发生,但如果发生:

  • 该脚本保留所有现有的类别分配
  • 检查是否有其他插件干扰
  • 从备份恢复并联系支持

自定义分类

此解决方案也适用于 custom taxonomies 使用 CPT UI 或类似插件创建。只需更改 'product_cat' 到你的自定义分类法蛞蝓:

// Example for custom taxonomy 'modality'
$terms = wp_get_post_terms($product_id, 'modality', ['fields' => 'ids']);
wp_set_object_terms($product_id, array_unique($all_terms), 'modality');

重要提示

  • 需要子主题: 始终使用子主题以防止在主题更新期间丢失代码
  • 备份优先: 在运行规范化脚本之前备份您的数据库
  • 在暂存环境上测试: Test on a staging site first if possible
  • 一次性脚本: 运行后删除标准化脚本
  • 保留第一部分代码: save_post_product hook 应永久保留

Summary

永久添加到 functions.php:

  • Auto-mark parent categories hook (Part 1)

运行一次并移除:

  • 规范化脚本(第二部分)

预期结果:

  • 父类别过滤工作正常
  • 子分类过滤继续工作
  • 层级钻取功能正常
  • 未来产品自动包含父类别