# Un JOIN inutile ajouté par un plugin tiers dans une requête WP_Query

> Pourquoi une requête WP_Query ordinaire traîne-t-elle soudain une jointure supplémentaire sur une table dont personne n'a besoin ? La réponse se trouve dans un filtre posts_join.

- Auteur : WordPress Développement
- Publié le : 2021-05-03
- Mis à jour le : 2021-05-03
- Catégorie : Performance
- URL : https://www.wpmoderne.fr/performance/join-inutile-plugin-tiers-wp-query/

## L’essentiel

- posts_join permet à n'importe quelle extension d'ajouter une jointure globale
- Le filtre s'applique même quand la requête n'a rien à voir avec le plugin
- Une condition sur la requête courante limite l'ajout au bon contexte

Pourquoi une simple requête d'archive de catégorie traîne-t-elle une jointure vers une table de statistiques de vues, alors que rien dans le code du thème ne le demande ? La réponse se trouve, dans ce cas précis, dans un plugin de comptage de vues d'articles qui utilise le filtre `posts_join` pour ajouter systématiquement une jointure vers sa propre table, sans jamais vérifier si la requête en cours a besoin de cette information.

Le filtre `posts_join` fait partie des points d'extension les plus puissants et les plus dangereux de `WP_Query` : il permet de modifier la clause `JOIN` de n'importe quelle requête générée, y compris celles qui n'ont rien à voir avec la fonctionnalité du plugin qui applique le filtre. Sans condition de garde appropriée, un plugin peut ainsi alourdir chaque requête du site, y compris dans l'administration ou lors d'un appel REST sans lien avec sa fonctionnalité.

## Repérer la jointure superflue

Query Monitor, dans son panneau des requêtes, affiche le SQL complet généré pour chaque appel à `WP_Query`, y compris les clauses ajoutées par des filtres tiers. Sur ce site, la même clause `JOIN wp_vues_articles ON wp_vues_articles.post_id = wp_posts.ID` apparaissait dans des requêtes d'archive, de recherche interne, et même dans la requête principale de la page d'accueil, alors que seule la page de détail d'un article affiche réellement le compteur de vues.

## Identifier le filtre responsable

```
// Extrait retrouvé dans le plugin tiers, simplifié
add_filter('posts_join', function ($join) {
    global $wpdb;
    $join .= " LEFT JOIN {$wpdb->prefix}vues_articles
                ON {$wpdb->prefix}vues_articles.post_id = {$wpdb->prefix}posts.ID";
    return $join;
});
```

Aucune vérification sur le type de requête, ni sur le contexte d'exécution : la jointure s'ajoute inconditionnellement, à chaque appel de `WP_Query` sur l'ensemble du site, y compris les requêtes internes de WordPress lui-même.

> L'essentiel à retenir : posts_join permet à n'importe quelle extension d'ajouter une jointure globale ; Le filtre s'applique même quand la requête n'a rien à voir avec le plugin ; Une condition sur la requête courante limite l'ajout au bon contexte

## Correctif appliqué côté thème

Sans toucher au code du plugin, il est possible de retirer un filtre déclaré de façon anonyme uniquement si l'on connaît sa priorité exacte, ce qui n'était pas le cas ici. La solution retenue a consisté à contacter l'éditeur du plugin pour signaler le problème, en attendant une correction, une condition de garde a été ajoutée côté thème sur une requête personnalisée sensible :

```
function requete_archive_sans_jointure_superflue(WP_Query $query) {
    if (!$query->is_main_query() || is_admin()) {
        return;
    }
    // Marqueur lu par le plugin corrigé en version suivante
    $query->set('sans_jointure_vues', true);
}
add_action('pre_get_posts', 'requete_archive_sans_jointure_superflue');
```

### Pourquoi la correction définitive doit venir du plugin

Une rustine côté thème ne peut jamais couvrir tous les points d'entrée où `WP_Query` est utilisée par d'autres extensions ou par le cœur lui-même. La correction propre consiste, côté plugin, à vérifier dans le filtre `posts_join` que la requête concerne effectivement un contexte où l'information de vues est utile, par exemple via `is_singular('post')` sur la requête principale du frontend.

## Prévention pour l'avenir

- Auditer, lors de l'installation d'un nouveau plugin, les filtres qu'il accroche sur `posts_join`, `posts_where` et `posts_clauses`.
- Comparer le SQL généré avant et après activation d'un plugin sur une même page de référence.
- Signaler à l'éditeur toute jointure globale non conditionnée, plutôt que de multiplier les correctifs côté thème.

## Estimer le coût réel de la jointure superflue

Pour quantifier l'impact avant de solliciter l'éditeur du plugin, un test comparatif a consisté à exécuter la requête de la page d'accueil, avec et sans la clause `JOIN` ajoutée, directement dans un client SQL, chronomètre en main sur plusieurs répétitions. L'écart mesuré restait modeste sur cette table de taille modérée, de l'ordre de 4 millisecondes par appel, mais ce coût se répétait sur chaque requête `WP_Query` de chaque page du site, y compris celles exécutées plusieurs fois par chargement pour des requêtes secondaires (widgets, articles liés). Cumulé sur l'ensemble d'une session de navigation, l'écart devenait perceptible.

## En résumé

Les filtres qui modifient directement le SQL de `WP_Query` offrent une puissance rarement nécessaire dans sa forme la plus large. Un plugin bien conçu limite toujours ce type de modification au contexte précis où elle est utile, faute de quoi c'est l'ensemble du site qui paie, discrètement, le coût d'une fonctionnalité dont la plupart des pages n'ont pas besoin.
