Le bloc « À lire aussi » en fin d’article reste l’un des moyens les plus simples d’augmenter le temps passé sur un site éditorial. Pas besoin d’extension pour ça : une requête WP_Query ciblant les taxonomies partagées avec l’article courant, associée à un peu de mise en cache, suffit largement. Cet article part du principe que les bases de meta_query sont déjà connues et se concentre sur la construction précise de cette requête liée.
Trois points méritent une attention particulière : cibler les bonnes taxonomies sans dupliquer inutilement les résultats, exclure systématiquement l’article en cours de lecture, et éviter de relancer la même requête à chaque affichage grâce à un transient.
Construire la requête sur les taxonomies partagées
Le paramètre tax_query de WP_Query permet de cibler les articles partageant au moins une catégorie avec l’article en cours. Récupérer d’abord les identifiants des catégories de l’article courant, puis les injecter dans la requête liée.
function magazine_articles_lies( $post_id, $nombre = 3 ) {
$categories = wp_get_post_categories( $post_id );
if ( empty( $categories ) ) {
return new WP_Query();
}
return new WP_Query( array(
'post_type' => 'post',
'posts_per_page' => $nombre,
'post__not_in' => array( $post_id ),
'ignore_sticky_posts' => true,
'tax_query' => array(
array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $categories,
),
),
) );
}
post__not_in exclut l’article courant, et ignore_sticky_posts évite qu’un article épinglé sans rapport avec le sujet ne s’invite dans une liste censée être thématique.
Combiner catégories et étiquettes

Se limiter aux catégories donne parfois des résultats trop larges sur un site avec peu de catégories mais beaucoup d’étiquettes fines. Une tax_query avec la relation OR entre catégories et étiquettes élargit la correspondance sans devenir trop permissive.
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $categories,
),
array(
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $etiquettes,
),
),
Pour prioriser malgré tout les correspondances par catégorie, il est possible de lancer d’abord une requête sur les catégories seules, puis de compléter avec une seconde requête sur les étiquettes uniquement si le nombre de résultats est insuffisant.
Mettre le résultat en cache avec un transient
Relancer cette requête à chaque affichage de l’article a un coût réel, en particulier sur les pages à fort trafic. L’API des transients permet de mettre le résultat en cache pendant une durée définie, sans dépendance externe.
function magazine_articles_lies_en_cache( $post_id, $nombre = 3 ) {
$cle = 'articles_lies_' . $post_id;
$ids = get_transient( $cle );
if ( false === $ids ) {
$requete = magazine_articles_lies( $post_id, $nombre );
$ids = wp_list_pluck( $requete->posts, 'ID' );
set_transient( $cle, $ids, DAY_IN_SECONDS );
}
return $ids;
}
Stocker uniquement les identifiants plutôt que les objets complets garde le transient léger et évite de sérialiser des objets WP_Post volumineux en base de données.
Invalider le cache à la publication d’un nouvel article
Un transient figé pendant 24 heures peut afficher des suggestions obsolètes si un article plus pertinent vient d’être publié dans la même catégorie. Le hook save_post permet de supprimer les transients concernés dès qu’un contenu change de catégorie ou est republié.
function magazine_invalider_cache_articles_lies( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) {
return;
}
global $wpdb;
$wpdb->query(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_articles_lies_%'"
);
}
add_action( 'save_post', 'magazine_invalider_cache_articles_lies' );
Cette purge globale reste volontairement simple ; sur un site à très fort volume de publication, une invalidation ciblée uniquement sur les articles de la même catégorie serait plus économe, au prix d’une logique un peu plus complexe.
Sur les sites où le contenu est publié en rafale certains jours, je réduis la durée du transient à quelques heures plutôt qu’à une journée entière : la fraîcheur des suggestions compte souvent plus que l’économie de requêtes.
En résumé
Une tax_query bien construite, une exclusion explicite de l’article courant et un transient correctement invalidé suffisent à obtenir un bloc « articles liés » performant, sans extension ni service externe. Le point le plus souvent négligé n’est pas la requête elle-même, mais l’invalidation du cache au bon moment.