HUSKY - Product Filter for WooCommerce

WooCommerceのカテゴリーフィルターが結果を返さない問題:カテゴリー階層の修正

WooCommerceのカテゴリーフィルターで親カテゴリーを指定しても結果が表示されず、商品が子カテゴリーに割り当てられている場合、このガイドが修正に役立ちます。

問題

症状:親カテゴリーでフィルターをかけると、その子カテゴリーに商品が存在するにもかかわらず、商品が表示されません。

例:

  • カテゴリー構造:Federal Actions > SEC > Enforcement
  • 商品の割り当て先:Enforcement(子カテゴリーのみ)
  • Federal ActionsまたはSECでフィルターした場合:結果なし
  • Enforcementでフィルターした場合:商品が表示される

原因

WordPress/WooCommerceでは、商品は階層内のすべての親カテゴリー(子カテゴリーだけでなく)で明示的にマークされる必要とします

WordPress管理画面で商品を子カテゴリーに割り当てると、WooCommerceは用語関係テーブルで商品にすべての親カテゴリーを自動的にマークしません。これはデータ構造の要件であり、HUSKYの制限ではありません。

正しいカテゴリーの割り当てには以下を含める必要があります:

  • 子カテゴリー:Enforcement
  • 親カテゴリー:SEC
  • 祖カテゴリー:Federal Actions

この問題があるかどうかを確認する方法

  1. 商品編集ページに移動します:wp-admin/post.php?post=YOUR_PRODUCT_ID&action=edit
  2. 右側の「カテゴリー」メタボックスを確認します
  3. 階層内のすべての親カテゴリーがチェックされているか確認します
  4. 子カテゴリーのみがチェックされている場合 → この問題があります

解決策:自動カテゴリー階層正規化

この解決策は2つの部分で構成されています:

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

このコードの動作:

  • 商品が保存されるたびにトリガーされます
  • 割り当てられたすべてのカテゴリーを取得します
  • 各割り当てカテゴリーのすべての親カテゴリーを見つけます
  • 商品を完全な階層でマークします
  • 関連するすべてのキャッシュをクリアします

結果: 今後、保存される商品には自動的にすべての親カテゴリーが含まれます。

パート2:既存商品の正規化(1回限りの修正)

このスクリプトはデータベース内の既存のすべての商品を修正します。この一時的なコードを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. コードをfunctions.phpに追加します
  2. 次のURLにアクセスします:https://yoursite.com/?normalize_cats=1(管理者としてログインしている必要があります)
  3. スクリプトが完了するまで待ちます
  4. 更新された商品数の結果が表示されます
  5. 1回実行したらこのコードを削除します

期待される出力:

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

これは、919商品のうち156商品に親カテゴリーが欠落しており、修正されたことを意味します。

修正のテスト

両方のパートを実装した後:

  1. HUSKYフィルターが設定されたショップページに移動します
  2. 親カテゴリーでフィルターしてみます
  3. 子カテゴリーの商品が表示されるはずです
  4. 階層をドリルダウンしてみます – すべてのレベルが機能するはずです

なぜHUSKYでこれをデフォルトにしないのか?

これは正当な質問です。理由は次のとおりです:

  1. データ構造の変更:これはWooCommerceのコアデータ関係を変更します
  2. 普遍性がない:一部のサイトでは特定のビジネスロジックのために意図的に子カテゴリーのみを割り当てています
  3. パフォーマンス:非常に大規模なカタログでは、商品保存ごとにオーバーヘッドが追加されます
  4. 競合:カテゴリー関係を管理する他のプラグインと干渉する可能性があります

将来の機能拡張:必要に応じてユーザーが有効にできるオプション設定としてHUSKYに追加する可能性がありますが、デフォルトの動作にはなりません。

代替案:手動でのカテゴリー割り当て

手動制御を希望する場合は、次のことができます:

  1. 各商品を編集します
  2. 「カテゴリー」メタボックスで、関連するすべての親カテゴリーを手動でチェックします
  3. 商品を保存します

これにより細かい制御が可能ですが、大規模なカタログでは時間がかかります。

トラブルシューティング

スクリプトが正規化された商品数0と表示する場合

  • 商品にすでに正しい親カテゴリーが割り当てられている可能性があります
  • いくつかの商品を手動で確認して検証します

正規化後もフィルターが機能しない場合

  • すべてのキャッシュをクリアします(WordPress、サーバー、ブラウザ)
  • HUSKYが正しいタクソノミー(product_cat)を使用していることを確認します
  • 他のフィルタープラグインとの競合を確認します
  • 商品が公開されていることを確認します(下書きではない)

子カテゴリーフィルターから商品が消える場合

これは発生すべきではありませんが、発生した場合:

  • スクリプトは既存のすべてのカテゴリー割り当てを保持します
  • 別のプラグインが干渉していないか確認します
  • バックアップから復元し、サポートに連絡します

カスタムタクソノミー

この解決策は、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');

重要な注意事項

  • 子テーマが必要:テーマ更新時のコード損失を防ぐために常に子テーマを使用します
  • 最初にバックアップ:正規化スクリプトを実行する前にデータベースをバックアップします
  • ステージングでテスト:可能であれば最初にステージングサイトでテストします
  • 1回限りのスクリプト:実行後に正規化スクリプトを削除します
  • パート1のコードを維持save_post_productフックは永続的に保持します

まとめ

functions.phpに永続的に追加:

  • 親カテゴリー自動マークフック(パート1)

1回実行して削除:

  • 正規化スクリプト(パート2)

期待される結果:

  • 親カテゴリーフィルターが正しく動作する
  • 子カテゴリーフィルターが引き続き動作する
  • 階層のドリルダウンが適切に機能する
  • 将来の商品に自動的に親カテゴリーが含まれる