Quand un attribut WooCommerce refuse d’apparaître dans le bloc « Filtrer par attribut », la cause est presque toujours la même : c’est un attribut personnalisé propre à un produit plutôt qu’une taxonomie globale pa_, ou bien il est global mais l’option « Activer les archives » est décochée. Le filtrage dans WooCommerce n’est pas une affaire d’affichage — c’est une requête de taxonomie doublée d’une lecture d’intervalle dans une table de recherche, pilotée par les variables filter_<slug>, query_type_<slug>, min_price et max_price. Une fois ce mécanisme compris, le reste s’explique : pourquoi les pages filtrées ratent votre cache, pourquoi les compteurs de facettes deviennent faux, pourquoi le curseur de prix s’écrase sur une seule valeur, et à partir de quelle taille de catalogue les filtres natifs cessent d’être le bon outil. Ce guide parcourt le mécanisme au complet et donne les vérifications à faire sur une boutique en production.
Ce que vous allez accomplir
- Lire une URL de catalogue filtrée et prédire exactement le SQL qu’elle produit.
- Confirmer que chaque attribut à filtrer est bien une taxonomie
pa_globale, avec archives activées et des termes réellement assignés. - Vérifier que le filtre de prix s’appuie sur une table
wc_product_meta_lookupremplie plutôt que sur un balayage de postmeta. - Traiter les chaînes de requête filtrées au niveau du cache pour que les facettes ne renvoient pas chaque affichage vers l’origine.
- Décider, avec un chiffre plutôt qu’une impression, quand passer des blocs de filtres natifs à une approche appuyée sur un index.
Prérequis
- WooCommerce 8.0 ou plus récent sur WordPress 6.4 ou plus récent. Les vieilles versions livrent le widget classique « Filtrer les produits par attribut » ; les versions actuelles livrent les blocs équivalents, et les versions récentes les regroupent dans un seul bloc de filtres. Les variables de requête décrites ici sont identiques dans les trois générations, ce qui rend ce guide valable pour les trois.
- Un thème de blocs, ou un thème classique avec une zone de widgets sur l’archive boutique. Les thèmes de blocs placent les filtres dans le gabarit du catalogue ; les thèmes classiques utilisent la barre latérale de la boutique.
- Un accès WP-CLI avec le paquet de commandes WooCommerce. Chaque sous-commande
wp wcpasse par les contrôleurs REST, donc elle exige--user=avec un identifiant d’administrateur. - Un accès à la base par
wp db query, et le droit de lire la configuration du cache (Nginx, Varnish ou une extension de cache). - Partez d’une copie de préproduction. Régénérer les tables de recherche sur un gros catalogue est une tâche de fond lourde en écritures.
Comment ça fonctionne
Une URL de catalogue filtrée porte tout son état dans la chaîne de requête. Rien n’est conservé côté serveur, et aucune règle de réécriture n’intervient.
Les filtres d’attribut. Pour un attribut global pa_color, le bloc émet ?filter_color=blue,red — soit le nom de la taxonomie amputé du préfixe pa_. WooCommerce en fait une clause tax_query sur pa_color. Une variable jumelle query_type_color choisit l’opérateur : and exige que le produit porte tous les termes retenus, or se contente d’un seul. Le choix se règle par filtre. or correspond à ce que le monde attend d’une couleur ou d’une taille ; and convient aux facettes indépendantes, et vide le résultat très vite quand on l’applique à des valeurs d’un même attribut.
C’est aussi pourquoi un attribut personnalisé ne peut jamais être filtré. Il vit sous forme de chaîne séparée par des barres verticales, dans une seule valeur postmeta sérialisée rattachée à un produit unique : pas de taxonomie, pas de termes, rien sur quoi tax_query puisse faire une jointure. La distinction, et la façon de convertir des produits déjà montés de travers, est traitée dans Produits variables et attributs WooCommerce. La seconde exigence est la case « Activer les archives ? » de l’attribut, stockée dans la colonne attribute_public de wp_woocommerce_attribute_taxonomies. Elle enregistre la taxonomie comme publique et donne à l’interface de filtre une cible de lien.
Le filtre de prix ne touche pas à postmeta. WooCommerce entretient wc_product_meta_lookup, une table plate d’une ligne par produit avec des colonnes min_price et max_price indexées, précisément pour qu’un filtre d’intervalle soit un BETWEEN sur des entiers indexés plutôt qu’un balayage d’une table de méta sérialisées. Pour un produit variable, min_price et max_price couvrent l’étendue de ses variations, ce qui rend le curseur de prix utilisable sur un catalogue de produits variables.
Les compteurs de facettes sont la partie coûteuse. Pour afficher « Bleu (42) », le filtre doit compter les produits correspondants pour chaque terme encore disponible, dans l’état de filtrage courant. WooCommerce met ce résultat en cache dans un transient dont le nom commence par wc_layered_nav_counts, une clé par taxonomie. Avec un cache objet persistant, le transient reste en mémoire ; sans lui, il devient une ligne de wp_options, avec la croissance de la table et une écriture en base à chaque combinaison de facettes encore froide. La mise en place d’un cache objet persistant est décrite dans Cache objet Redis pour WordPress.
Le problème de cache est structurel, pas une erreur de configuration. Les caches de pages complètes indexent sur l’URL entière, chaîne de requête comprise : ?filter_color=blue et ?filter_color=blue,red sont deux entrées distinctes — et la plupart des piles sont réglées pour contourner complètement le cache dès qu’un paramètre inconnu apparaît. Chaque clic sur une facette aboutit donc dans PHP et MySQL.
Pas à pas
-
Listez vos attributs et vérifiez l’indicateur d’archives. Le champ
has_archivesest le nom d’API de la case « Activer les archives ? ».wp wc product_attribute list --user=1 --fields=id,name,slug,type,has_archivesVous devriez voir une ligne par attribut global, avec des
slugdu genrepa_color. Tout attribut destiné au filtrage doit afficherhas_archivesà1outrue. Si un attribut attendu est carrément absent, c’est un attribut personnalisé et il ne peut pas être filtré. -
Activez les archives sur l’attribut qui en manque. Remplacez par l’identifiant relevé à l’étape précédente.
wp wc product_attribute update 3 --user=1 --has_archives=trueLa commande affiche
Success: Updated product_attribute 3.Videz ensuite les règles de réécriture avecwp rewrite flush --hardpour que la nouvelle taxonomie publique se résolve. -
Confirmez que des termes sont bel et bien assignés à des produits publiés. Un attribut aux archives activées mais sans aucune assignation affiche un bloc de filtre vide.
PREFIX=$(wp db prefix) wp db query "SELECT t.name, tt.count FROM ${PREFIX}term_taxonomy tt JOIN ${PREFIX}terms t ON t.term_id = tt.term_id WHERE tt.taxonomy = 'pa_color' ORDER BY tt.count DESC;"Attendez-vous à une liste de noms de termes avec des comptes non nuls. Un terme dont le compte est
0n’apparaîtra pas dans le filtre, à moins d’avoir désactivé le masquage des termes vides. -
Vérifiez que la table de recherche des prix est remplie. Les lignes dont
min_pricevautNULLsont des produits invisibles pour le filtre de prix.wp db query "SELECT COUNT(*) AS total, SUM(min_price IS NULL) AS missing_min, MIN(min_price), MAX(max_price) FROM $(wp db prefix)wc_product_meta_lookup;"missing_mindevrait valoir0, et le couple min/max devrait correspondre aux bornes réelles de votre catalogue. Sitotalest nettement sous votre nombre de produits publiés, la table est périmée. -
Régénérez la table de recherche si elle est périmée. Trouvez d’abord l’identifiant de l’outil au lieu de le deviner.
wp wc tool list --user=1 --fields=id,name wp wc tool run regenerate_product_lookup_tables --user=1L’exécution renvoie une réponse de succès confirmant que la régénération est planifiée. Elle passe par Action Scheduler : sur un gros catalogue, laissez la file se vider —
wp action-scheduler run, ou l’écran des actions planifiées — avant de refaire l’étape 4. -
Sollicitez les variables de requête directement. C’est la façon la plus rapide de prouver le mécanisme sans cliquer dans le thème.
curl -s "https://example.com/shop/?filter_color=blue&query_type_color=or" | grep -c 'class="product' curl -s "https://example.com/shop/?filter_color=blue,red&query_type_color=and" | grep -c 'class="product'La variante
ordevrait renvoyer au moins autant de correspondances que la varianteand. Si les deux renvoient le même nombre et que ce nombre égale la taille de page non filtrée, le filtre ne s’applique pas — la requête est probablement servie depuis une copie en cache de l’archive non filtrée. -
Décidez délibérément du traitement des paramètres de filtre par le cache. Contourner le cache sur les paramètres de filtre connus garde les résultats filtrés justes sans laisser des paramètres inventés fragmenter le cache. Sur Nginx avec un
fastcgi_cache, réglez la variable de contournement à partir des noms de paramètres :set $skip_cache 0; if ($arg_filter_color != "") { set $skip_cache 1; } if ($arg_min_price != "") { set $skip_cache 1; } if ($arg_max_price != "") { set $skip_cache 1; } fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache;Après un rechargement par
nginx -t && systemctl reload nginx, une requête filtrée devrait renvoyer un en-tête de cacheMISSouBYPASS, pendant que l’URL/shop/toute simple renvoie encoreHIT. Le réglage plus large du catalogue est traité dans Optimisation des performances WooCommerce. -
Inspectez et videz les transients de compteurs de facettes. Ce sont les lignes qui gonflent en l’absence de cache objet persistant.
wp db query "SELECT option_name, LENGTH(option_value) AS bytes FROM $(wp db prefix)options WHERE option_name LIKE '%wc_layered_nav_counts%';" wp transient delete --allLa première requête liste un transient par taxonomie filtrée. Après la suppression,
wp transient delete --allindique combien de transients ont été retirés, et le prochain affichage filtré reconstruit les compteurs. -
Traitez la surface SEO. Les URL filtrées sont des quasi-doublons de l’archive et ne devraient pas lui faire concurrence. Confirmez que la balise canonique pointe vers l’archive non filtrée, puis tenez les robots hors de l’espace combinatoire avec une règle
robots.txt:User-agent: * Disallow: /*?filter_ Disallow: /*&filter_ Disallow: /*?min_price=Vérifiez avec
curl -s "https://example.com/shop/?filter_color=blue" | grep canonical— l’URL affichée devrait êtrehttps://example.com/shop/, pas la variante filtrée. Les règles de canonique et d’indexation à l’échelle du catalogue sont couvertes dans SEO WooCommerce pour les catalogues de produits.
Vérifier que ça fonctionne
Lancez ceci sur l’archive en ligne, une fois les changements faits.
curl -s -o /dev/null -w "%{time_starttransfer}\n" "https://example.com/shop/"
curl -s -o /dev/null -w "%{time_starttransfer}\n" "https://example.com/shop/?filter_color=blue&min_price=20&max_price=80"
L’URL non filtrée devrait être rapide et servie par le cache ; la filtrée sera plus lente parce qu’elle atteint PHP, et c’est voulu. Ce qui compte, c’est l’écart : si la requête filtrée traîne de plus d’une seconde environ, le problème vient de la requête elle-même, pas du contournement de cache.
wp db query "EXPLAIN SELECT product_id FROM $(wp db prefix)wc_product_meta_lookup WHERE min_price >= 20 AND max_price <= 80;"
La colonne key devrait nommer un index plutôt qu’afficher NULL. Une clé NULL sur une grosse table signifie que les index de la table de recherche n’ont pas été créés et que le filtre de prix fait un balayage complet.
Confirmez enfin que les deux filtres se combinent. Choisir une couleur devrait faire diminuer les compteurs affichés à côté de la facette de taille, et le bloc Filtres actifs devrait lister les deux sélections avec des liens de retrait fonctionnels.
Dépannage
L’attribut est absent du bloc Filtrer par attribut. Soit c’est un attribut personnalisé (aucune taxonomie pa_ n’existe), soit has_archives est faux, soit tous les termes ont un compte de zéro. L’étape 1 départage les trois cas en une commande. À noter : un attribut servant uniquement aux variations se filtre très bien tant qu’il est global et que ses archives sont activées — « utilisé pour les variations » et « filtrable » sont deux réglages indépendants.
Choisir deux valeurs du même attribut ne renvoie rien. Le type de requête est and, qui exige qu’un seul produit porte les deux termes. Un t-shirt est rarement bleu et rouge à la fois. Passez cette facette à or dans les réglages du bloc ; gardez and pour les facettes de type caractéristiques ou certifications, où plusieurs valeurs cohabitent légitimement sur un produit.
Le curseur de prix affiche le même chiffre aux deux bouts. wc_product_meta_lookup est périmée ou partiellement remplie — souvent après une importation en lot, une migration de prix, ou la restauration d’une sauvegarde qui omettait les tables de recherche. Faites les étapes 4 et 5. Tant que la régénération n’est pas terminée, chaque produit variable importé sans reconstruction de prix déclare un minimum NULL et se retrouve exclu des résultats d’intervalle.
Les compteurs de facettes contredisent la grille de produits. Les comptes proviennent d’un transient bâti dans un autre état de filtrage, ou avant un changement de stock. Videz-les comme à l’étape 8. Si l’écart revient sans cesse, la cause est habituellement une importation qui reporte la mise à jour des comptes de taxonomie et laisse wp_term_taxonomy.count faux ; wp term recount pa_color corrige les comptes de termes eux-mêmes.
Le temps de réponse s’effondre juste après l’ajout des filtres. C’est le contournement de cache qui fait son travail, plus le trafic de robots qui explorent les combinaisons de facettes. Confirmez-le dans votre journal d’accès : une salve de chaînes de requête distinctes venant d’un même agent utilisateur, c’est un robot, et les règles robots.txt de l’étape 9 règlent le cas. Si le trafic est réellement humain, vous avez atteint le plafond décrit plus bas.
Quand ce n’est pas la bonne approche
Les filtres natifs de WooCommerce sont le bon outil pour quelques milliers de produits et une poignée de facettes. Au-delà, le coût ne vient pas de la requête de taxonomie : il vient des compteurs. Chaque facette de la page réclame une requête de comptage par terme, dans l’état de filtrage courant, et cela grimpe avec le produit des cardinalités. Une boutique avec six facettes de vingt termes chacune demande cent vingt requêtes de comptage sur un cache froid.
Quand votre archive filtrée contient des dizaines de milliers de produits, ou que la clientèle arrive par la recherche plutôt que par la navigation dans les catégories, déplacez les facettes vers un index. ElasticPress ou une extension de filtrage dédiée comparable calcule facettes et compteurs à même une seule requête d’index, ce pour quoi les moteurs de recherche sont bâtis. Les compromis sont réels : un service de plus à opérer et à surveiller, une réindexation à chaque enregistrement de produit, et une fenêtre où l’index retarde sur la base après un changement de prix en lot. N’adoptez pas ça pour un catalogue de deux mille produits — vous héritez du coût d’exploitation sans le bénéfice.
Les filtres sont aussi la mauvaise réponse quand le vrai besoin est une meilleure structure de catégories : une combinaison de facettes qui attire du trafic mérite une page de catégorie indexable avec son propre contenu, pas une chaîne de requête. Et si la boutique est lente avant même qu’un filtre soit appliqué, réglez ça d’abord. La documentation officielle WooCommerce couvre les options des blocs, et la référence des taxonomies WordPress couvre l’API qui les sous-tend.
Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.