Chapitre 5.5 — WP_Query & les requêtes de contenu
⏱️ TL;DR —
WP_Queryest l’objet qui interroge les contenus (wp_posts) via un tableau d’arguments déclaratifs (post_type,posts_per_page,meta_query,tax_query,orderby…), sans écrire de SQL. Trois façons de l’utiliser : la main query (automatique, celle de la page), une requête secondaire (new WP_Query(...)), ou les helpers (get_posts()). Pour modifier la requête de la page, on n’en crée pas une nouvelle : on filtrepre_get_posts. Bien manierWP_Query(et éviter ses pièges de perf) est central en développement.
🎯 Objectifs
- Construire une requête de contenu avec
WP_Query. - Filtrer par métadonnées (
meta_query) et taxonomies (tax_query). - Modifier la main query proprement via
pre_get_posts. - Éviter les pièges de performance (requêtes coûteuses,
posts_per_page => -1).
1. Trois façons d’obtenir des contenus
- Main query : celle de la page (URL), déjà exécutée ; on la parcourt via la Boucle.
new WP_Query( $args ): une requête que vous formulez (articles liés, carrousel…). Bouclez dessus puiswp_reset_postdata().get_posts( $args ): un helper qui retourne directement un tableau de posts (pratique hors Boucle).
2. Les arguments essentiels
$args = array(
'post_type' => 'post', // ou 'page', 'livre', array(...)
'post_status' => 'publish',
'posts_per_page' => 10, // -1 = tous (⚠️ voir pièges)
'orderby' => 'date', // 'title', 'menu_order', 'meta_value_num', 'rand'…
'order' => 'DESC',
'paged' => get_query_var( 'paged' ) ?: 1,
'category_name' => 'actus',
's' => 'terme de recherche',
);
$q = new WP_Query( $args );Quelques familles d’arguments : type/statut, pagination (posts_per_page, paged, offset), tri (orderby, order), filtre par auteur, date, taxonomie, métadonnée.
💡 Pour un dev React —
WP_Queryest un query builder déclaratif (façon Prisma/ORM) : vous décrivez quoi récupérer via un objet d’arguments, WordPress génère le SQL, gère la pagination et le cache.get_posts()≈findMany(). La logique « décris ta requête, pas ton SQL » vous est familière — les noms d’arguments diffèrent, l’intuition non.
3. Filtrer par métadonnées et taxonomies
// Livres à moins de 20 €, du genre "roman"
$args = array(
'post_type' => 'livre',
'meta_query' => array(
array(
'key' => 'prix',
'value' => 20,
'type' => 'NUMERIC',
'compare' => '<=',
),
),
'tax_query' => array(
array(
'taxonomy' => 'genre',
'field' => 'slug',
'terms' => 'roman',
),
),
);meta_query: filtre surwp_postmeta(clé/valeur, opérateurs=,<=,LIKE,IN,BETWEEN…).tax_query: filtre sur les taxonomies (par slug/id/name), combinable enAND/OR.
⚠️ Piège n°1 — les
meta_querycoûteuses. Filtrer/trier sur des métadonnées implique des jointures surwp_postmeta(modèle EAV, ch. 4.6), vite lourdes sur de gros volumes. Pour des filtres fréquents et critiques (e-commerce, annuaires), on envisage des tables dédiées, des taxonomies (indexées, plus rapides que meta pour du filtrage), ou un moteur de recherche externe. Ne construisez pas un catalogue de 100 000 produits filtré uniquement parmeta_query.
4. Modifier la main query : pre_get_posts
Pour changer ce qu’affiche une page (ex. « 12 articles par page sur les archives », « inclure les CPT dans la recherche »), ne créez pas une nouvelle requête dans le thème : filtrez la main query avant son exécution.
add_action( 'pre_get_posts', 'monprefixe_ajuster_query' );
function monprefixe_ajuster_query( WP_Query $query ) {
// uniquement la requête principale, côté front, sur les archives
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_archive() ) {
$query->set( 'posts_per_page', 12 );
}
}⚠️ Piège n°2 — modifier toutes les requêtes. Sans les gardes
! is_admin()et$query->is_main_query(), votre filtre s’applique partout (admin inclus, requêtes secondaires) → effets de bord graves (listes d’admin cassées, widgets faussés). Toujours cibler précisément.
5. Pièges de performance (récap)
posts_per_page => -1: charge tous les contenus en mémoire. Dangereux sur de gros volumes (OOM). Paginez ou limitez.meta_querylourdes : voir §3.- Requêtes dans une boucle (N+1) : ne relancez pas une requête par élément affiché — préparez les données en amont.
no_found_rows => true: si vous n’avez pas besoin de la pagination totale, cet argument évite unCOUNT(*)coûteux.fields => 'ids': ne récupérer que les IDs quand c’est suffisant, plus léger.- Le cache : les résultats sont mis en cache objet (ch. 5.7) ; sur des requêtes lourdes récurrentes, un transient peut aider.
📚 Aller plus loin —
WP_Queryaccepte des dizaines d’arguments (dates,post__in,post_parent,meta_keypour tri,fields,update_post_meta_cache…). Consultez la doc du cœur ; mais la règle d’or perf tient en une phrase : ne demandez que ce dont vous avez besoin (bonspost_type,posts_per_page,fields,no_found_rows).
✏️ Exercices
- Écrivez les arguments d’un
WP_Querypour « les 5 derniers articles publiés du typelivre, triés par date décroissante ». - Vous voulez afficher 12 éléments par page sur les archives. Pourquoi utiliser
pre_get_postsplutôt qu’unnew WP_Querydans le template ? - Citez deux raisons pour lesquelles
posts_per_page => -1est risqué.
✅ Solution
$args = array(
'post_type' => 'livre',
'post_status' => 'publish',
'posts_per_page' => 5,
'orderby' => 'date',
'order' => 'DESC',
);- Parce que la page a déjà une main query (pagination, contexte, SEO) ;
pre_get_postsajuste cette requête existante (pagination correcte, une seule requête). Unnew WP_Querydans le template crée une requête en double, casse la pagination native et le contexte. - Il charge tous les contenus en mémoire (risque de dépassement mémoire / lenteur) et empêche toute pagination ; dangereux dès que le volume grandit.
🧠 Quiz de révision
1. À quoi sert WP_Query ?
À interroger les contenus (wp_posts) via des arguments déclaratifs, sans écrire de SQL.
2. Comment modifier la requête de la page en cours proprement ?
Via le hook pre_get_posts, en ciblant is_main_query() et ! is_admin().
3. Que filtrent meta_query et tax_query ?
meta_query filtre sur les métadonnées (wp_postmeta) ; tax_query sur les taxonomies.
4. Pourquoi les meta_query peuvent-elles être lentes ?
Elles impliquent des jointures sur wp_postmeta (modèle EAV), coûteuses à grande échelle.
5. Quel argument évite un COUNT(*) inutile ?
no_found_rows => true (quand la pagination totale n’est pas nécessaire).
Chapitre suivant : Base de données : $wpdb.