Langue

Me contacter

Extension WordPress · depuis 2014

MDTF

Recherche à facettes pour les articles WordPress et les types de contenu personnalisés, par champs post meta et taxonomies à la fois.

Première version
Installations actives
1 000+
Note
4.5 / 5 (28)Lire les avis
  • PHP
  • WordPress
  • WP_Query
  • meta_query
  • tax_query
  • Transients API
  • Sessions PHP
  • MySQL
  • PDO
  • jQuery
  • ion.rangeSlider
  • WooCommerce
  • WPML

À propos

MDTF filtre n'importe quel type de contenu par champs meta et taxonomies ensemble. Il a été conçu pour des catalogues qui ne sont pas des boutiques : immobilier, concessionnaires automobiles, annuaires. Le propriétaire du site définit les champs de filtre dans un constructeur intégré (case à cocher, liste déroulante, curseur de plage, liste déroulante de plage, calendrier, saisie de texte, libellé), les renseigne pour chaque contenu, et place des formulaires de recherche en widgets ou en shortcodes.

La difficulté est que WordPress stocke les meta sous forme de chaînes non typées dans wp_postmeta. Chaque condition de plage, de date et de texte doit être transformée en une meta_query qui compare correctement, chaque option visible a besoin d'un compte en direct dans la recherche courante, et les AND/OR entre groupes ne doivent jamais affaiblir les conditions qui masquent des contenus.

L'extension est maintenue depuis 2014, à travers PHP 7.x et 8.x, les changements de stockage de WooCommerce 3.0, l'éditeur de widgets en blocs, et une longue série de signalements de sécurité traités via Patchstack et Wordfence. Les branches premium et gratuite partagent la même base de code.

MDTF · filtre de métadonnées

ce qui a été résolu

EN LIGNEdepuis 2014

État de la recherche et liens

shipped · 2014

Ce que cela apporteLes liens des résultats de recherche restent courts et peuvent être mis en favori ou partagés. Répéter la même recherche réutilise un seul lien au lieu d'accumuler des copies stockées.

Problème
Une recherche sur de nombreux champs meta et taxonomies est une grande structure imbriquée. Transportée dans des paramètres GET, elle produit des liens très longs et expose la forme de la requête, et chaque requête de la page (résultats, compteurs, pagination, tri, recomptage ajax) doit lire le même état.
Solution

L'état du formulaire est encodé en JSON, stocké sur le serveur dans un transient ou une session PHP, et le lien ne porte qu'une clé courte. Un accesseur unique le décode et le valide pour toutes les requêtes de la requête HTTP.

Stack
  • PHP
  • Transients API
  • Sessions PHP
  • admin-ajax
Tâches · 5
L'état de la recherche est trop grand pour voyager dans l'URLStocker du JSON en base64 sur le serveur, ne mettre qu'une courte clé page_mdf dans le lien

encode_search_get_params() reçoit le formulaire sérialisé par ajax, l'analyse et l'assainit, retire les valeurs '~' (any) et stocke le reste en JSON base64 avec set_page_mdf_session(). Selon le réglage keep_search_data_in, les données vont dans un transient pour un jour ou dans $_SESSION. La réponse est mdf_cat=...&page_mdf=<key>, que le front office transforme en lien des résultats.

La même recherche créait une nouvelle copie stockée à chaque foisLa clé est le md5 d'un JSON normalisé : clés associatives triées, scalaires nettoyés, listes gardées dans l'ordre

get_search_data_key() hache la sortie de normalize_search_data(), qui parcourt le tableau récursivement, trie les clés associatives et conserve l'ordre des listes. L'ordre des champs venant du navigateur n'a plus d'importance, si bien qu'une recherche correspond à un transient et à un lien. Les liens avec d'anciennes clés continuent de fonctionner tant que leur transient existe.

Un point d'accès public stocke chaque recherche pendant un jourRejeter les entrées non textuelles ou trop volumineuses (filtrable, 65536 octets) avant tout stockage

mdf_encode_search_get_params est enregistré pour les visiteurs non connectés, et chaque recherche distincte écrit un transient qui vit un jour. Le gestionnaire vérifie que vars est une chaîne de longueur inférieure à mdf_search_data_max_length et, sinon, répond mdf_cat=-1 sans rien stocker. Une clé fournie n'est acceptée que si elle correspond à /^[0-9a-f]{32}$/, sinon la clé est dérivée des données.

L'état stocké n'est pas fiable lorsqu'il est reluDécoder en tableau ou en rien ; forcer en nombres les bornes from/to des plages

get_page_mdf_data() traite tout ce qui ne se décode pas en tableau comme une absence de recherche. Elle fusionne les options par défaut du widget et, pour chaque champ medafi_, convertit 'from' et 'to' en nombres, si bien que les valeurs de plage et de calendrier atteignent meta_query sous forme de nombres quoi que le visiteur ait envoyé. Le résultat décodé est conservé dans une variable statique pour le reste de la requête.

Les recherches fixes ont besoin de liens permanents qui n'expirent jamaisUne clé numérique renvoie à la post meta page_mdf_string au lieu d'un transient

Les liens constants sont un type de contenu caché distinct. Son save_post() stocke la recherche encodée dans page_mdf_string. get_page_mdf_session() vérifie la clé : numérique signifie un ID de post et lit la meta stockée, tout le reste va au stockage par transient ou par session. Les résultats, les compteurs et les templates n'ont pas besoin de savoir quel type de lien a été utilisé.

Construction des meta queries

shipped · 2014

Ce que cela apporteLes visiteurs peuvent chercher par prix, année, kilométrage, dates ou mots, y compris avec des plages ouvertes comme « et plus », et les filtres peuvent réutiliser des données déjà saisies sur les contenus.

Problème
Les valeurs meta sont des chaînes dans wp_postmeta. Les prix, années, kilométrages, plages de dates et textes libres doivent devenir des clauses meta_query qui comparent comme des nombres ou des dates, prennent en charge les plages ouvertes, et que MySQL puisse encore exécuter.
Solution

Chaque type d'élément de filtre a sa propre traduction en meta_query ou en fragment posts_where, avec des comparaisons DECIMAL pour les curseurs et les listes déroulantes de plage, des bornes supérieures ouvertes et des modèles de plage à deux clés stockés par contenu.

Stack
  • PHP
  • WP_Meta_Query
  • posts_where
  • MySQL
Tâches · 5
Un curseur à son maximum doit signifier « et plus », pas « jusqu'au maximum »Remplacer la borne haute par 2147483647 lorsque la poignée est au maximum configuré

Dans get_meta_query_array(), le cas du curseur compare le 'to' sélectionné au maximum propre au curseur ; s'ils sont égaux, la borne supérieure devient MetaDataFilterCore::$max_int, si bien que les contenus dont les valeurs dépassent l'échelle configurée correspondent toujours. Les listes déroulantes de plage utilisent la même idée avec un suffixe 'i' (infini) tel que '1001-i'. Les curseurs simples et les listes déroulantes de plage utilisent BETWEEN avec le type DECIMAL.

L'élément a une plage de valeurs, le visiteur cherche un seul nombreCurseur à double valeur : key <= v AND key_to >= v sur deux champs meta

Pour les curseurs avec slider_range_value = 1, un contenu stocke deux valeurs, la clé du champ et key_to (par exemple une fourchette de prix ou de surface). Le visiteur choisit une valeur, et la requête exige qu'elle tombe dans la fourchette propre au contenu, les deux côtés typés DECIMAL.

Plages de dates dont l'une des extrémités du calendrier peut être videQuatre branches sur les clés _from/_to : début ouvert, fin ouverte, jour unique, BETWEEN

L'élément calendrier stocke <key>_from et <key>_to. Seul 'to' renseigné donne _to <= to ; seul 'from' renseigné donne _from >= from ; des valeurs égales correspondent aux éléments dont la période couvre ce jour ; une vraie période devient BETWEEN sur _from. Un commentaire dans le code date la dernière branche du 08-10-2014.

Saisie de texte cherchant dans le titre ou le contenu mot par motposts_where ajoute un LIKE par mot via $wpdb->prepare et esc_like, assemblés par un filtre

Un élément texte peut cibler sa propre valeur meta ou post_title, post_content, ou les deux avec OR ou AND. Pour les colonnes du post, la condition est transmise aux filtres MDTF_HELPER sur posts_where, qui découpent l'expression en mots (modifiable avec mdf_txtsearch_some_words_behavior) et les assemblent avec mdf_txtsearch_some_words_condition, AND par défaut. Les espaces répétées sont d'abord réduites.

Les champs de filtre doivent réutiliser des meta déjà présentes sur le siteLes éléments reflétés associent une clé de filtre à une clé meta existante au moment de la requête

Un champ marqué is_reflected porte reflected_key. Le constructeur de requêtes, le séparateur OR et l'API publique get_field() utilisent tous la clé reflétée, si bien qu'un filtre peut travailler sur des meta écrites par le thème ou une autre extension sans copier les valeurs dans des clés medafi_.

Logique AND/OR entre groupes de filtres

dans le produit

Ce que cela apporteLes visiteurs peuvent choisir si les résultats correspondent à l'une ou à toutes les options qu'ils cochent. Les éléments cachés, en rupture de stock ou sans rapport restent hors des résultats.

Problème
Les visiteurs peuvent vouloir un OR entre les options cochées, mais un simple OR au sommet de meta_query combine aussi en OR les conditions de service : catégorie du filtre, contenus cachés, visibilité WooCommerce et stock. Des contenus cachés et étrangers se retrouvent alors dans les résultats. Le mode choisi doit aussi atteindre chaque requête d'une recherche.
Solution

Les conditions sont séparées en conditions du visiteur, qui peuvent être combinées en OR, et conditions obligatoires. La requête finale est un AND au niveau supérieur avec un groupe OR imbriqué, pour les meta comme pour les taxonomies. Le mode voyage dans l'état de recherche stocké.

Stack
  • PHP
  • WP_Meta_Query
  • WP_Tax_Query
Tâches · 6
Un simple OR combinerait aussi en OR les conditions qui masquent des contenuswrap_meta_query() : AND au sommet, groupe OR du visiteur imbriqué, conditions de service obligatoires

wrap_meta_query() construit une relation AND et imbrique les conditions combinables en OR dans un groupe de relation OR, seulement s'il y en a deux ou plus. mdf_hide_post NOT EXISTS, meta_data_filter_cat, _visibility et _stock_status vont au niveau supérieur et s'appliquent toujours. La même enveloppe est utilisée par la requête de résultats et par les compteurs.

Seules les conditions de cases à cocher et de libellés ont un sens sous ORsplit_meta_query_array() type chaque clause selon son élément de filtre ; les curseurs restent obligatoires

Le séparateur associe chaque clé de recherche (propre ou reflétée) à son type d'élément à partir des sections de filtre de la recherche. Les clauses des éléments case à cocher et libellé deviennent combinables en OR ; les curseurs, listes déroulantes, saisies de texte, calendriers et listes déroulantes de plage restent obligatoires. En mode AND, le tableau est renvoyé inchangé, si bien que les recherches AND existantes produisent le même SQL qu'avant.

Le OR sur les taxonomies ne doit pas supprimer la visibilité ni les taxonomies supplémentairessplit_tax_query_array() sépare les termes du visiteur de ceux ajoutés par les filtres, par identité

Une fois que des filtres tiers (mdf_filter_taxonomies, taxonomies supplémentaires, product_visibility) ont modifié la tax_query, le séparateur compare chaque clause à la liste construite à partir du formulaire avec array_search strict. Les correspondances sont des conditions du visiteur, le reste est obligatoire. Les clauses répétées du visiteur que le filtre a collectées par requête sont supprimées. wrap_tax_query() n'imbrique ensuite sous OR que la partie du visiteur.

Le mode AND/OR doit atteindre le recomptage, la pagination et le triStocker le mode dans les options de widget page_mdf, n'utiliser la requête qu'en repli

encode_search_get_params() écrit meta_data_filter_bool et mdf_tax_bool dans mdf_widget_options de la recherche stockée, en prenant le choix du visiteur lorsque le widget autorise les deux. get_meta_data_filter_bool() et get_mdf_tax_bool() lisent d'abord la valeur stockée et normalisent en 'AND' tout ce qui n'est pas 'OR'. Les résultats, les compteurs, le recomptage ajax, la pagination et le tri lisent le même état.

Activer le OR sur les taxonomies ne doit pas changer les résultats des widgets déjà présents sur des sites en productionN'appliquer le OR sur les taxonomies qu'aux widgets enregistrés avec un indicateur de version ; les plus anciens restent en AND

Les widgets enregistrés par d'anciennes versions ont toujours cherché dans les taxonomies avec AND, quoi que dise leur option OR. Leur appliquer l'option changerait les résultats sur des sites en production sans aucune action du propriétaire. get_widget_tax_bool() ne respecte l'option que si taxonomies_options_behaviour_ver est défini, ce qui arrive quand le widget est enregistré par la nouvelle version.

Un parent coché et son enfant coché : le parent correspondrait à tout ce qui est en dessousÉcarter un terme coché lorsqu'un de ses enfants est aussi coché

tax_query inclut les enfants par défaut ; un parent coché correspondrait donc à tous les contenus en dessous et rendrait la sélection de l'enfant sans objet. get_tax_query_array() retire un terme coché si get_term_children() de ce terme croise l'ensemble coché, puis réindexe la liste.

Compteurs dynamiques et cache

shipped · 2014

Ce que cela apporteChaque option indique combien d'éléments elle renverrait, si bien que les visiteurs évitent les résultats vides. Les comptes sont mis en cache, le site reste donc rapide.

Problème
Chaque case à cocher, option de liste déroulante et terme affiche le nombre de contenus qu'il renverrait dans la recherche courante. Cela fait une requête par élément visible, avec des jointures sur wp_postmeta, à chaque affichage de page et à chaque recomptage ajax.
Solution

Les requêtes de comptage ne récupèrent que des ID et sautent found-rows et le préchargement du cache. Les comptes peuvent être mis en cache dans une table clé/valeur indexée par un hachage de la requête, et les arbres de termes dans des transients de durée définie.

Stack
  • PHP
  • WP_Query
  • MySQL
  • Transients API
Tâches · 5
Un compte doit montrer la recherche plus cette seule optionPar élément : ajouter sa clause, remplacer la valeur sœur du même select, relancer

get_item_posts_count() part de la recherche courante. Une case à cocher ajoute key = 1 si elle n'y est pas déjà. Une option de liste déroulante retire d'abord la clause de la même clé, car les options sœurs s'excluent mutuellement, puis s'ajoute elle-même (sous forme de BETWEEN pour les options de plage). Pour les termes, get_tax_count() injecte le terme dans une copie de l'état de la taxonomie avec le cache de taxonomie par requête désactivé, et le restaure ensuite.

Les requêtes de comptage payaient pour des données qu'elles n'utilisaient jamaisfields=ids, nopaging, pas de préchargement du cache meta/termes, set_found_posts() surchargée

Les requêtes de comptage ne demandent que les ID, avec update_post_meta_cache, update_post_term_cache et cache_results désactivés. WP_QueryMDFCounterIn étend WP_Query et surcharge set_found_posts() pour ne rien faire, si bien que WordPress n'exécute pas l'étape found-rows supplémentaire ; post_count est la réponse.

Les mêmes comptes recalculés à chaque affichage de pageMettre les comptes en cache dans {prefix}mdf_query_cache, indexés par le md5 des arguments de la requête

WP_QueryMDFCounter hache le tableau complet d'arguments et le cherche dans une table à deux colonnes dotée d'un index sur mkey ; en cas d'absence, elle exécute la requête et insère le compte. La page de réglages crée la table et désactive l'option, en affichant l'instruction CREATE, si l'utilisateur de la base n'a pas le privilège. Un administrateur vide le cache depuis la page de réglages, ce qui exécute un TRUNCATE derrière manage_options et un nonce.

Les listes hiérarchiques de termes coûtent une requête par niveauUn seul appel get_categories() par taxonomie, arbre construit en PHP, mis en cache en transient

get_terms() charge une fois tous les termes de la taxonomie et assemble_terms_childs() construit les listes d'enfants à partir de ce tableau. Avec cache_terms_data activé, le résultat est stocké sous mdf_terms_cache_<md5 des arguments> dans un transient dont la durée vient du réglage de la période du cache des termes (de une heure à 7 jours ; sans expiration si aucune période n'est définie).

Le filtre ne doit pas se charger sur les pages sans rechercheListe d'autorisation d'URL facultative : sauter l'initialisation du front office si le chemin n'y figure pas

is_should_init() s'exécute avant que l'extension n'enregistre ses types de contenu et ses hooks. Si init_on_pages_only est défini, elle normalise l'URL courante (chaîne de requête et /page/N retirées) et retourne tôt si l'URL ne figure pas dans la liste. Les requêtes d'administration s'initialisent toujours.

Modèle de données et intégration à WordPress

shipped · 2014

Ce que cela apporteLes résultats apparaissent dans le design existant du site, et le personnel peut renommer ou réorganiser les champs de filtre sans perdre les données déjà enregistrées sur les contenus.

Problème
Les champs de filtre sont définis par le propriétaire du site, leurs valeurs vivent dans les post meta de types de contenu quelconques, et les résultats doivent apparaître dans les templates propres au thème sans modifier le thème.
Solution

Les définitions de champs vivent dans un type de contenu caché. Les valeurs des contenus sont enregistrées deux fois, comme un seul tableau pour l'édition et comme clés meta séparées pour l'interrogation. La requête principale est remplacée depuis pre_get_posts avec une protection contre la réentrance.

Stack
  • PHP
  • Hooks WordPress
  • WP_Query
  • WooCommerce
  • WPML
Tâches · 6
Les valeurs meta doivent être modifiables en bloc et interrogeables une à uneEnregistrer l'ensemble comme un seul tableau et chaque champ medafi_ comme sa propre clé meta

save_post() écrit page_meta_data_filter comme un seul tableau, que l'écran d'édition relit, puis écrit chaque champ medafi_ avec update_post_meta(), que meta_query peut utiliser. Les sections de filtre et leurs éléments sont stockés comme meta html_items du type de contenu caché meta_data_filter.

Changer la catégorie de filtre d'un contenu ne doit pas laisser de valeurs périméesAu changement de catégorie, supprimer toutes les meta medafi_ du contenu avant d'enregistrer les nouvelles

Les champs de l'ancienne catégorie de filtre resteraient sinon dans wp_postmeta et correspondraient aux recherches de cette catégorie. save_post() compare la catégorie envoyée à celle qui est stockée et supprime toutes les lignes medafi_% du contenu lorsqu'elle a changé.

Renommer la clé d'un champ ne doit pas orpheliner les données des contenusVérifier l'unicité dans toutes les sections de filtre, puis renommer dans postmeta avec un seul UPDATE

change_meta_key() exige le préfixe medafi_, rejette une clé déjà utilisée dans cette section ou dans toute autre section de filtre et nomme la section en conflit. Elle renomme ensuite la clé dans html_items, réécrit les tableaux page_meta_data_filter des contenus concernés et exécute UPDATE postmeta SET meta_key. Elle exige un nonce, edit_post et edit_others_posts, car elle touche tous les contenus.

Les résultats doivent s'afficher dans le template de recherche propre au thèmepre_get_posts exécute la requête de résultats et remplace le $wp_query global, avec une protection contre les boucles

parse_query() marque la requête principale comme une recherche du type de contenu demandé. pre_get_posts() affiche [meta_data_filter_results] sans template ; cette vue déclare global $wp_query et lui assigne un nouveau WP_Query construit à partir de la recherche. Un indicateur de requête empêche la réentrance, car la nouvelle requête redéclenche pre_get_posts. Le filtre mdtf_prohibited_post_types garde hors de ce mécanisme les requêtes internes d'ACF et de Contact Form 7.

WooCommerce 3.0 a déplacé les produits cachés des meta vers une taxonomieBranchement par version : product_visibility NOT IN pour 3.0+, meta _visibility avant

Les résultats, les compteurs d'éléments et les compteurs de termes se branchent tous sur WOOCOMMERCE_VERSION : à partir de 3.0, ils ajoutent une tax_query qui exclut exclude-from-catalog, les versions antérieures reçoivent une clause meta _visibility IN (catalog, visible). Une clause _stock_status facultative masque les produits en rupture de stock. Avec WPML actif, chaque requête reçoit aussi la langue courante.

Les anciennes options de liste déroulante n'ont pas de clé, alors que les contenus stockent leurs titres comme valeursfill_select_keys() utilise le titre de l'option comme clé à la lecture, en correspondance avec les valeurs stockées des contenus

Les options de liste déroulante enregistrées par d'anciennes versions du constructeur ont une clé vide, alors que la meta box du contenu stocke le titre de l'option comme valeur. Au lieu d'une migration, fill_select_keys() s'exécute dans get_html_items() et à l'enregistrement, remplit les clés vides avec le titre nettoyé et laisse intactes les clés explicites, y compris '0'. Les valeurs existantes des contenus correspondent sans qu'il faille réenregistrer les contenus.

Durcissement de la sécurité

dans le produit

Ce que cela apporteLa recherche publique ne peut pas servir à écrire des données indésirables ni à atteindre des actions d'administration, ce qui protège le site et son contenu.

Problème
L'extension expose des points d'accès ajax publics, affiche des templates par leur nom à partir d'options, et écrit des post meta à partir de formulaires.
Solution

Signatures côté serveur pour les données renvoyées par le navigateur, listes blanches pour les chemins de templates et les clés meta, et contrôles de capacités et de nonce sur les actions d'administration.

Stack
  • PHP
  • Nonces WordPress
  • wp_hash
Tâches · 4
Un widget dans un bloc doit être redessiné par le recomptage ajax à partir d'options portées par la pageSigner la classe et les options du widget avec wp_hash ; n'accepter que deux classes de widgets connues

Un Legacy Widget dans un bloc n'a pas de barre latérale où le recomptage pourrait le trouver par son ID, son formulaire porte donc la classe et les options du widget. get_widget_redraw_data() les signe avec wp_hash() ; get_widget_from_redraw_data() vérifie avec hash_equals() et n'accepte que MetaDataFilter_Search et MetaDataFilter_TaxSearch avant d'appeler the_widget().

Le chemin du template de résultats vient des options du shortcode et du widgetRéduire chaque segment du chemin à [A-Za-z0-9_-] ; sous-dossiers autorisés, '..' impossible

sanitize_template_path() normalise les barres obliques, filtre chaque segment vers un jeu de caractères sûr et supprime les segments vides, de sorte qu'une valeur ne peut pas sortir du dossier des templates tandis que des sous-dossiers comme any/my_posts continuent de fonctionner.

Le formulaire d'édition d'un contenu ne doit pas écrire d'autres clés meta que les champs de filtreN'accepter que les clés medafi_ et exiger edit_post avant d'enregistrer les valeurs

save_post() retire du tableau envoyé toute clé sans le préfixe medafi_ avant de le stocker et revérifie le préfixe avant chaque appel à update_post_meta(), de sorte que le formulaire ne peut pas définir _edit_lock, _price ou des clés similaires. L'enregistrement des valeurs exige current_user_can('edit_post').

Les actions ajax d'administration ne doivent pas être appelables par n'importe quel visiteurCapacité plus nonce pour le vidage du cache, le renommage de clé et l'édition de widget

Le vidage du cache exige manage_options et mdtf_ajax_nonce. Le renommage de clé exige son propre nonce, edit_post et edit_others_posts. Les actions sur les champs de widget exigent edit_theme_options. Les visiteurs non connectés n'atteignent que les actions de recherche du front office (enfants de termes, encodage de la recherche, recomptage, panneau de recherche, contenu du bouton de recherche). Les autres gestionnaires enregistrés pour eux vérifient eux-mêmes l'accès : les actions d'abonnement du messager exigent une connexion et un nonce, l'assistant de statistiques exige manage_options.

Chiffres

wordpress.org
  • Installations actives 1 000+ compteur de wordpress.org.
  • Avis 28 Moyenne de 4.5 sur 5.
  • Années de maintenance 12 Depuis 22 avril 2014, dernière mise à jour 13 octobre 2026.