HUSKY - Product Filter for WooCommerce

Filtro de WooCommerce por categoría no devuelve resultados: Solución de jerarquía de categorías

Si el filtro de productos por categoría en WooCommerce no devuelve resultados para las categorías principales, aunque haya productos asignados a categorías secundarias, esta guía te ayudará a solucionarlo.

El problema

Síntoma: Al filtrar por una categoría principal, no se muestran productos, aunque existan productos en sus categorías secundarias.

Ejemplo:

  • Estructura de categorías: Federal Actions > SEC > Enforcement
  • Producto asignado a: Enforcement (solo categoría secundaria)
  • Filtrar por Federal Actions o SEC devuelve: Sin resultados
  • Filtrar por Enforcement devuelve: Producto mostrado

Por qué ocurre esto

WordPress/WooCommerce requiere que los productos estén marcados explícitamente con todas las categorías principales en la jerarquía, no solo con la categoría secundaria.

Cuando asignas un producto a una categoría secundaria a través de la interfaz de administración de WordPress, WooCommerce no marca automáticamente el producto con todas las categorías principales en la tabla de relaciones de términos. Esto es un requisito de la estructura de datos, no una limitación de HUSKY.

La asignación correcta de categorías debería incluir:

  • Categoría secundaria: Enforcement
  • Categoría principal: SEC
  • Categoría abuela: Federal Actions

Cómo comprobar si tienes este problema

  1. Ve a la página de edición de un producto: wp-admin/post.php?post=YOUR_PRODUCT_ID&action=edit
  2. Mira la metabox de Categorías en el lado derecho
  3. Comprueba si todas las categorías principales de la jerarquía están marcadas
  4. Si solo está marcada la categoría secundaria → Tienes este problema

Solución: Normalización automática de la jerarquía de categorías

Esta solución consta de dos partes:

  1. Solución a futuro: Marcar automáticamente las categorías principales al guardar productos
  2. Solución inmediata: Normalizar todos los productos existentes

Parte 1: Marcar automáticamente las categorías principales (productos futuros)

Añade este código al functions.php de tu tema hijo:

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

Qué hace esto:

  • Se activa cada vez que se guarda un producto
  • Obtiene todas las categorías asignadas
  • Encuentra todas las categorías principales de cada categoría asignada
  • Marca el producto con la jerarquía completa
  • Limpia todas las cachés relevantes

Resultado: A partir de ahora, cualquier producto guardado incluirá automáticamente todas las categorías principales.

Parte 2: Normalizar productos existentes (solución única)

Este script corregirá todos los productos existentes en tu base de datos. Añade este código temporal a tu 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
    ];
}

Cómo usarlo:

  1. Añade el código a tu functions.php
  2. Ve a: https://yoursite.com/?normalize_cats=1 (debes haber iniciado sesión como administrador)
  3. Espera a que el script se complete
  4. Verás los resultados que muestran cuántos productos se actualizaron
  5. Elimina este código después de ejecutarlo una vez

Salida esperada:

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

Esto significa que 156 productos de 919 tenían categorías principales faltantes y han sido corregidos.

Prueba de la solución

Después de implementar ambas partes:

  1. Ve a tu página de tienda con los filtros HUSKY
  2. Intenta filtrar por una categoría principal
  3. Los productos de las categorías secundarias ahora deberían aparecer
  4. Intenta profundizar en la jerarquía: todos los niveles deberían funcionar

¿Por qué no hacer esto predeterminado en HUSKY?

Es una pregunta legítima. Aquí está el motivo:

  1. Cambio en la estructura de datos: Esto modifica las relaciones de datos principales de WooCommerce
  2. No es universal: Algunos sitios asignan intencionadamente solo categorías secundarias por lógica de negocio específica
  3. Rendimiento: En catálogos muy grandes, esto añade sobrecarga en cada guardado de producto
  4. Conflictos: Puede interferir con otros plugins que gestionan las relaciones de categorías

Mejora futura: Podríamos añadir esto como una configuración opcional en HUSKY que los usuarios puedan habilitar si es necesario, pero no será el comportamiento predeterminado.

Alternativa: Asignación manual de categorías

Si prefieres el control manual, puedes:

  1. Editar cada producto
  2. En la metabox de Categorías, marca todas las categorías principales relevantes manualmente
  3. Guardar el producto

Esto te da un control granular pero requiere mucho tiempo para catálogos grandes.

Solución de problemas

El script muestra 0 productos normalizados

  • Tus productos ya podrían tener las categorías principales asignadas correctamente
  • Revisa algunos productos manualmente para verificarlo

El filtrado sigue sin funcionar después de la normalización

  • Limpia todas las cachés (WordPress, servidor, navegador)
  • Asegúrate de que HUSKY esté usando la taxonomía correcta (product_cat)
  • Comprueba si hay conflictos con otros plugins de filtrado
  • Verifica que los productos estén publicados (no en borrador)

Los productos desaparecen del filtro de categoría secundaria

Esto no debería ocurrir, pero si sucede:

  • El script conserva todas las asignaciones de categorías existentes
  • Comprueba si otro plugin está interfiriendo
  • Restaura desde una copia de seguridad y contacta al soporte

Taxonomías personalizadas

Esta solución también funciona para taxonomías personalizadas creadas con CPT UI o plugins similares. Simplemente cambia 'product_cat' por el slug de tu taxonomía personalizada:

// 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');

Notas importantes

  • Tema hijo obligatorio: Usa siempre un tema hijo para evitar la pérdida de código durante las actualizaciones del tema
  • Respaldar primero: Haz una copia de seguridad de tu base de datos antes de ejecutar el script de normalización
  • Probar en un entorno de pruebas: Prueba en un sitio de prueba si es posible
  • Script de una sola vez: Elimina el script de normalización después de ejecutarlo
  • Conservar el código de la Parte 1: El hook save_post_product debe permanecer de forma permanente

Resumen

Añadir a functions.php de forma permanente:

  • Hook para marcar automáticamente las categorías principales (Parte 1)

Ejecutar una vez y eliminar:

  • Script de normalización (Parte 2)

Resultados esperados:

  • El filtrado por categoría principal funciona correctamente
  • El filtrado por categoría secundaria sigue funcionando
  • La navegación jerárquica funciona correctamente
  • Los productos futuros incluyen automáticamente las categorías principales