Architecture
websource-facetedsearch.php fichier principal : constantes, chargeur PSR-4, compatibilité HPOS, amorçage
src/Plugin.php amorçage (inerte sans WooCommerce), activation, désactivation
src/Settings.php réglages (option wsx_facetedsearch_settings)
src/Templates.php modèles de filtres (option wsx_facetedsearch_templates), types, styles, résolution
src/Context.php page en cours : type, terme, recherche, URL de base
src/Url.php format d'URL des filtres
src/Engine.php sélection, conditions SQL, valeurs et compteurs
src/Renderer.php rendu HTML du bloc et des pastilles
src/Frontend.php requête principale, emplacement, blocs, codes courts, SEO
src/Cache.php, Tasks.php mémoire, invalidation, préparation planifiée
src/Rest.php, Admin.php API REST, écran de réglages
src/Swatches.php couleur et texture des valeurs d'attributs
src/ThemePackage.php export / import par paquet de thème
Espace de noms : Websource\FacetedSearch. Préfixes : options wsx_facetedsearch_*, transients wsx_fs_*, classes CSS wsfs-*.
Sources de données : wc_product_meta_lookup (prix, stock, promotion, note), wc_product_attributes_lookup (attributs, stock par variation ; repli sur term_relationships si la table est absente, vide ou en reconstruction), term_relationships (catégories, marques, étiquettes, visibilité).
Blocs
| Bloc | Nom | Attributs | Rendu |
|---|---|---|---|
| Filtres produits (Websource) | websource/facets | aucun (supports : align, spacing) | Bloc de filtres de la liste en cours. Vide hors liste de produits. Une seule instance par page. |
| Filtres actifs (Websource) | websource/active-filters | aucun (supports : align, spacing) | Pastilles des filtres appliqués. |
Les deux blocs sont rendus côté serveur (render_callback), déclarés par blocks/*/block.json, sans étape de build.
Codes courts
| Code court | Équivalent | Attributs |
|---|---|---|
[websource_facets] | bloc websource/facets | aucun |
[websource_active_filters] | bloc websource/active-filters | aucun |
Emplacement automatique
Quand le réglage auto_place est actif :
- thèmes classiques : actions
woocommerce_before_shop_loop(priorité 1) etwoocommerce_after_shop_loop(priorité 999), ainsi quewoocommerce_no_products_found; - thèmes blocs : filtres
render_block_woocommerce/product-collectionetrender_block_core/query(priorité 1000), pour les blocs qui héritent de la requête de la page.
Structure produite :
<div class="wsfs-layout wsfs-layout--d-sidebar wsfs-layout--m-drawer">
<aside class="wsfs-aside"><div id="wsfs-root" class="wsfs" data-wsfs>…</div></aside>
<div class="wsfs-main" data-wsfs-main><div class="wsfs-active" data-wsfs-active>…</div> …liste…</div>
</div>
Format d'URL
Un seul paramètre, wsf par défaut :
/boutique/?wsf=marques:nordik/couleur:bleu,vert/prix:10-50
/sépare les filtres,:suit le nom du filtre,,sépare les valeurs.- Nom du filtre : titre du filtre passé par
sanitize_title(marques,couleur,prix…). La clé technique (brand,attr_couleur,price…) est aussi acceptée. - Valeurs : identifiant (slug) du terme ; pour les options, titre de l'option (
en-stock,en-promotion…) ou clé (in_stock,promo…) ; pour le prix,min-maxdans la devise affichée. - Les filtres sont toujours écrits dans l'ordre du modèle : une sélection = une seule URL.
orderby,setpost_typesont conservés. La pagination revient à la page 1 quand un filtre change.- Sans JavaScript, le formulaire de prix envoie
wsfs[prix][min]etwsfs[prix][max]; le serveur redirige (302) vers l'URL propre.
SEO des pages filtrées : noindex, follow (filtre wp_robots, en-tête X-Robots-Tag), <link rel="canonical"> vers la liste sans filtre. Si tous les filtres actifs sont « indexables » et que leur nombre ne dépasse pas max_indexable_filters, la page reste indexable avec un canonical vers elle-même. Yoast SEO et Rank Math sont pris en compte (wpseo_canonical, wpseo_robots, rank_math/frontend/canonical).
Hooks et filtres
Filtres
| Filtre | Paramètres | Rôle |
|---|---|---|
wsx_facetedsearch_query_var | string $name (wsf) | Nom du paramètre d'URL ([a-z][a-z0-9_]*). |
wsx_facetedsearch_active_filters | array $filters, array $template, string $pageType | Filtres actifs d'une page, indexés par clé, dans l'ordre. |
wsx_facetedsearch_scope_where | string $where, Context $context | Clause WHERE du périmètre (alias p = articles, l = wc_product_meta_lookup). |
wsx_facetedsearch_price_factor | float $factor | Rapport entre le prix affiché et le prix stocké (TVA). |
wsx_facetedsearch_is_color_attribute | bool $isColor, string $taxonomy | Indique si un attribut est une couleur (style par défaut « pastilles »). |
wsx_facetedsearch_swatch | array $swatch, int $termId, string $slug | Couleur et texture d'une valeur : ['color' => '#rrggbb', 'texture' => 'https://…']. |
wsx_facetedsearch_indexable | bool $indexable, Engine $engine | Décision d'indexation d'une page filtrée. |
wsx_facetedsearch_facets_html | string $html, Engine $engine | HTML final du bloc de filtres. |
wsx_facetedsearch_filter_url | string $url, array $args | URL filtrée demandée par une autre extension (voir ci-dessous). |
websource_theme_export | array $sections, array $context | Ajoute la section facetedsearch (version 1) à un paquet de thème. |
websource_theme_import | ?array $report, string $key, array $section, array $context | Importe la section facetedsearch (mode : replace ou merge, dry_run). |
Lien filtré pour une barre de recherche :
$url = apply_filters('wsx_facetedsearch_filter_url', $lienNatif, ['filter' => 'brand', 'id' => 17]);
// http://exemple.test/boutique/?wsf=marques:nordik (ou $lienNatif si le filtre Marques est inactif)
filter vaut brand (ou manufacturer) ou category ; pour une catégorie, la réponse est la page de la catégorie.
Retirer un filtre sur une catégorie précise :
add_filter('wsx_facetedsearch_active_filters', function (array $filters, array $template, string $pageType) {
if (is_product_category('papeterie')) {
unset($filters['attr_taille']);
}
return $filters;
}, 10, 3);
Évènement JavaScript
Après chaque mise à jour sans rechargement, document reçoit wsfs:updated :
document.addEventListener('wsfs:updated', function (event) {
console.log(event.detail.url, event.detail.total);
});
Invalidation
La mémoire est invalidée (numéro de version incrémenté) sur : woocommerce_new_product, woocommerce_update_product, woocommerce_update_product_variation, woocommerce_product_set_stock, woocommerce_variation_set_stock, woocommerce_product_set_stock_status, woocommerce_variation_set_stock_status, woocommerce_delete_product, woocommerce_trash_product, woocommerce_attribute_added|updated|deleted, wc_after_products_starting_sales, wc_after_products_ending_sales, transition_post_status (produits), created_term, edited_term, delete_term (taxonomies de produits), comment_post (avis), wp_set_comment_status.
Depuis votre code : \Websource\FacetedSearch\Cache::bump();
Tâche planifiée
Évènement WP-Cron wsx_facetedsearch_warmup (récurrence wsx_fs_warmup, réglage warmup_minutes) : calcule les filtres sans sélection de la boutique et des warmup_top catégories les plus fournies.
Routes REST
Espace de noms : websource-facetedsearch/v1.
| Méthode | Chemin | Accès | Rôle |
|---|---|---|---|
| GET | /facets | public | Filtres, compteurs et total d'une liste. |
| GET | /preview | manage_woocommerce | HTML du bloc de filtres (aperçu de l'administration). |
| GET | /status | manage_woocommerce | État des tables de recherche, de la mémoire et de la préparation. |
| POST | /cache/flush | manage_woocommerce | Vide la mémoire. |
| POST | /warmup | manage_woocommerce | Lance la préparation maintenant. |
| POST | /reindex | manage_woocommerce | Demande à WooCommerce de reconstruire ses tables de recherche. |
| GET | /templates/recommended | manage_woocommerce | Modèle conseillé calculé d'après le catalogue. |
Paramètres de /facets et /preview :
| Paramètre | Type | Défaut | Rôle |
|---|---|---|---|
type | shop, category, brand, tag, search | shop | Type de page. |
term | entier | 0 | Identifiant du terme (catégorie, marque, étiquette). |
search | texte | vide | Mots recherchés. |
filters | texte | vide | Sélection, au format du paramètre d'URL. |
Exemple :
GET /wp-json/websource-facetedsearch/v1/facets?type=category&term=28&filters=taille:m
Réponse (extrait : un seul filtre, deux valeurs) :
{
"total": 5,
"url": "http://127.0.0.1:8306/product-category/vetements/?wsf=taille:m",
"filtered": true,
"active": [
{
"facet": "Taille",
"label": "M",
"url": "http://127.0.0.1:8306/product-category/vetements/"
}
],
"facets": [
{
"key": "attr_taille",
"type": "attribute",
"title": "Taille",
"url_key": "taille",
"widget": "checkbox",
"multiple": true,
"collapsed": false,
"show_limit": 10,
"searchable": false,
"indexable": false,
"nofollow": true,
"show_counts": true,
"slider_mode": "double",
"has_active": true,
"range": false,
"values": [
{
"id": 26,
"slug": "l",
"label": "L",
"count": 4,
"active": false,
"disabled": false,
"soldout": false,
"color": "",
"texture": "",
"url": "http://127.0.0.1:8306/product-category/vetements/?wsf=taille:m,l"
},
{
"id": 25,
"slug": "m",
"label": "M",
"count": 4,
"active": true,
"disabled": false,
"soldout": false,
"color": "",
"texture": "",
"url": "http://127.0.0.1:8306/product-category/vetements/"
}
]
}
]
}
Les routes d'administration demandent un utilisateur connecté et l'en-tête X-WP-Nonce (ou un mot de passe d'application). Sans droits, la réponse est 401.
POST /wp-json/websource-facetedsearch/v1/cache/flush
→ {"deleted": 12, "products": 16, "indexed": 16, "attribute_lookup": true, "attribute_rows": 30, "cache_version": 8, …}
Variables CSS
Le bloc reprend les variables du thème Websource, avec une valeur de repli pour tout autre thème.
| Variable du bloc | Variable du thème | Repli | Rôle |
|---|---|---|---|
--wsfs-brand | --color-brand-600 | #0f766e | Couleur principale (cases cochées, curseur, bouton). |
--wsfs-brand-soft | --color-brand-50 | #effaf8 | Fond des compteurs et des pastilles. |
--wsfs-ink | --color-ink | #14282b | Texte. |
--wsfs-line | --color-line | #d9e6e4 | Bordures et séparateurs. |
--wsfs-surface | --color-surface | #fff | Fond du panneau. |
--wsfs-r | --radius-xl | 1rem | Arrondi des champs et boutons. |
--wsfs-w | — | min(22rem, 88vw) | Largeur du tiroir et du volet. |
| — | --radius-2xl | 1.5rem | Arrondi du panneau. |
| — | --wst-card-shadow | none | Ombre du panneau. |
Fichiers chargés uniquement sur les listes qui affichent des filtres : assets/front.css, assets/front.js (différé, sans jQuery), et assets/push.css seulement si un mode « volet » est utilisé.
Fonctions publiques
| Appel | Rôle |
|---|---|
Frontend::engine(): ?Engine | Moteur de la liste de la page en cours (null hors liste filtrable). |
Frontend::engineFor(Context $context, array $state): ?Engine | Moteur d'un contexte quelconque. |
Context::forShop(), Context::forTerm(int $id, string $taxonomy = 'product_cat') | Fabriques de contexte. |
Engine::total(): int | Nombre de produits de la sélection. |
Engine::facets(): array | Filtres avec leurs valeurs (id, slug, label, count, active, disabled, soldout, color, texture) ou leur plage (min, max, step, selected_min, selected_max). |
Engine::state(): array | Sélection en cours, dans l'ordre du modèle. |
Engine::mainQueryWhere(string $idExpr): string | Conditions SQL de la sélection pour une requête de produits. |
Url::build(string $base, array $kept, array $state): string | Construit une URL filtrée. |
Url::serialize(array $state), Url::unserialize(string $encoded) | Encode et décode le paramètre d'URL. |
Templates::all(), Templates::resolve(string $pageType, array $ancestorIds) | Modèles enregistrés, modèle d'une page. |
Settings::get(string $name), Settings::pageType(string $type) | Lecture des réglages. |
Cache::bump(), Cache::flush() | Invalide ou vide la mémoire. |
Exemple : nombre de produits bleus de la boutique.
use Websource\FacetedSearch\{Context, Frontend};
$engine = Frontend::engineFor(Context::forShop(), ['couleur' => ['bleu']]);
$total = $engine ? $engine->total() : 0;
Données enregistrées
| Emplacement | Contenu |
|---|---|
Option wsx_facetedsearch_settings | Réglages. |
Option wsx_facetedsearch_templates | Modèles de filtres. |
Option wsx_facetedsearch_cache_version | Version de la mémoire. |
Option wsx_facetedsearch_tasks | Dernière préparation. |
Transients wsx_fs_* | Filtres calculés. |
Métadonnées de terme wsx_color, wsx_texture | Couleur et texture des valeurs d'attributs. |
Aucune table n'est créée. La désinstallation supprime tout ce qui précède.
Écarts par rapport au module PrestaShop
- Pas de multiboutique (une installation WordPress = une boutique).
- Pas d'index de prix propre ni de file d'attente : les tables de recherche de WooCommerce jouent ce rôle. Les tâches se réduisent à la préparation de la mémoire (WP-Cron) ; il n'y a ni URL de cron à jeton ni déclenchement « sans cron ».
- Types de page : boutique, catégories, marques, étiquettes, recherche. Fournisseurs, nouveaux produits, meilleures ventes et promotions n'ont pas de page native dans WooCommerce.
- Filtres « État » (neuf, occasion) et « Caractéristiques » : pas d'équivalent natif. Les attributs WooCommerce couvrent les caractéristiques ; le curseur sur caractéristique numérique n'est pas repris. Le filtre « Étiquettes » est ajouté.
- « Soldes » devient « Mis en avant » (produits mis en avant de WooCommerce).
- Format d'URL :
wsf=marques:nordik/couleur:bleu,vertau lieu deq=marques-nordik/couleur-bleu-vert, parce que les identifiants WordPress contiennent des tirets. - Titres de filtres par langue : un seul titre, traduit par Polylang ou WPML.
- Une sélection de plusieurs attributs s'applique au produit, pas à une même variation.