Chapitre 17 sur 20
Requêtes avancées avec WP_Query et WPDB
La plupart du temps, nous travaillons avec des fonctions nous permettant de simplement récupérer le contenu dont nous avons besoin : la loop
, get_posts
, get_comments
, get_users
, get_taxonomies
, etc. Toutes ces fonctions peuvent recevoir des paramètres permettant de définir des critères de recherche.
Cependant, dans certains cas, nos besoins sont plus spécifiques : recherche ou filtrage complexe, requête sur plusieurs tables ou sur une table custom… C’est la qu’interviennent les custom queries et l’objet WPDB !
Requêtes avancées avec WP_Query
C’est l’objet que WordPress utilise sous le capot lorsque vous invoquez have_posts
, get_posts
, etc. De ce fait, les options de filtres offertes par l’ensemble des fonctions de récupération de données de WordPress découlent de WP_Query
.
Ainsi, la plupart du temps, vous pourrez utiliser l’API de WP_Query
avec les fonctions classiques. On se rabattra sur cette dernière seulement si la requête n’est pas réalisable avec les fonctions plus spécifiques. Par ailleurs, WP_Query
dispose de nombreuses méthodes pour récupérer les données ou agir sur la requête.
Par exemple, get_posts
nous permet de récupérer une liste de posts. dont on peut spécifier l’ordre et filtrer par de nombreux critères : liste d’auteurs, de catégories ou de tags à inclure ou exclure, intervale de dates, etc.
// On requêtes tous les posts dont le slug de la taxonomie "movie_genre" est "action" ou "comedy"
// ET dont les acteurs (taxonomie) ne contiennent pas les acteurs d'ID 103, 115 et 206
$query = new WP_Query([
'post_type' => 'post',
'tax_query' => [
'relation' => 'AND',
[
'taxonomy' => 'movie_genre',
'field' => 'slug',
'terms' => ['action', 'comedy']
],
[
'taxonomy' => 'actor',
'field' => 'term_id',
'terms' => [103, 115, 206],
'operator' => 'NOT IN'
]
]
]);
On aperçoit ici la puissance des requêtes. Mais on peut faire bien plus. Il est possible de requêter via des métas et aussi d’imbriquer les conditions sur les taxonomies.
// Récupère les biens immobiliers de type maison
// avec une superficie comprise entre 150 et 200m2
$query = new WP_Query([
'post_type' => 'property',
'tax_query' => [
[
'taxonomy' => 'type',
'field' => 'name',
'terms' => ['house']
]
],
'meta_query' => [
'key' => 'area',
'value' => [150, 200],
'compare' => 'BETWEEN',
'type' => 'NUMERIC'
]
]);
// On récupère en général les résultats avec get_posts
$results = $query->get_posts();
Pour chacun des exemples, on récupérerait en général les résultats avec get_posts
, mais les autres méthodes have_post
et the_post
que l’on utilise souvent comme fonction globale sont en réalité des méthodes de WP_Query
que l’on peut utiliser sur toute instance afin de travailler avec les résultats.
Query dans une query
Lorsque l’on effectue une query alors qu’une autre est en cours (par exemple dans la loop), notre custom query va perturber la query globale.
Afin de ne pas avoir de bug, dès que l’on a terminé le traitement de notre query custom, on restore l’objet query principal. On utilise pour cela wp_reset_postdata
.
Bien entendu, dans la mesure où la custom query est exécuté en dehors de toute autre requête, il n’y a rien à faire.
Je pense que vous commencez à comprendre la constructions des requêtes avancées et les possibilités qu’elles ouvrent. C’est aussi ces fonctions qui font la puissance de WordPress en tant que framework.
Le cas WooCommerce
Même WooCommerce utilise WP_Query
sous le capot. Cependant, il est recommandé d’utiliser wc_get_products
afin de se prémunir de futurs changements dans la façon dont les données sont stockées.
Pour les requêtes sur les attributs, le nom de l’attribut est toujours préfixé par pa_
(product attribute). De ce fait, le nom de l’attribut color
est pa_color
.
// tax query pour les matières (material)
'tax_query' => [[
'taxonomy' => 'pa_material',
'field' => 'term_id',
'terms' => […],
'operator' => 'IN'
]]
Par ailleurs, par défaut, wc_get_products
ne supporte pas les requêtes sur les métas qu’offre WP_Query
. Il suffit de quelques lignes pour en ajouter le support.
add_filter('woocommerce_product_data_store_cpt_get_products_query', function ($query, $query_vars) {
if (!empty($query_vars['meta_query'])) {
$query['meta_query'] = $query_vars['meta_query'];
}
return $query;
}, 10, 2);
En plus de wc_get_products
, WooCommerce fournit deux autres fonctions très utiles : wc_get_orders
et WC_Order_Query
, dont vous trouverez la documentation sur GitHub.
Requêtes SQL avec wpdb
Nous l’avons constaté, il est possible de créer des requêtes vraiment très puissante avec l’ORM de WP. Cependant, il arrive parfois que l’on ait besoin d’un outil plus bas niveau pour, par exemple, gérer des tables custom ou optimiser des requêtes complexes. C’est là que wpdb
entre en scène.
Dans sa forme la plus simple, wpdb
permet d’executer une requête SQL et d’en récupérer les résultats.
global $wpdb;
$results = $wpdb->get_results("SELECT * FROM 'wp_options'");
Par défaut, les résultats sont sous forme d’objet, mais il est possible comme souvent d’obtenir un tableau associatif ou indexé. Il suffit pour cela de passer une des trois constantes en second argument : OBJECT
, ARRAY_A
, ARRAY_N
.
À ce stade, vous avez peut-être déjà remarqué que mettre en dur le nom des tables, sachant qu’elles sont préfixées de manière unique à chaque site, risque de poser des problèmes : de portabilité notamment. Évidemment, tout est prévu car wpdb met à notre disposition une propriété par table afin d’automatiquement en récupérer le nom.
global $wpdb;
$results = $wpdb->get_results("SELECT * FROM {$wpdb->options}");
Évidemment, cela va légèrement se compliquer dès lors que l’on travaille avec des tables non standard. Cependant, $wpdb
met également à notre disposition la propriété $base_prefix
. De ce fait, si l’on souhaite mentionner une table appelée my_custom_table
on peut la référencer sans se soucier du préfixe en utilisant $base_prefix
.
global $wpdb;
$results = $wpdb->get_results("SELECT * FROM {$wpdb->base_prefix}my_custom_table");
Nous avons ici seulement mentionné la méthode get_results
, mais il en existe trois autres permettant de récupérer des données :
get_var
pour ne récupérer qu’une seule valeur,get_row
pour récupérer une seule ligne,get_col
pour récupérer une colonne.
À vous de voir laquelle est la plus adaptée à votre besoin. wpdb
propose des méthodes pour les opérations les plus courantes (insert
, update
, delete
) ainsi que des propriétés permettant d’accéder aux informations utiles. Nous avons vu $base_prefix
, mais on peut avoir besoin du $insert_id
par exemple, ou de consulter $last_error
.
En dernier lieu, nous pouvons utiliser la méthode générique query
afin d’exécuter n’importe quelle requête. Cette méthode est la plus bas niveau offerte par wpdb
et s’avère utile lorsqu’une requête très spécifique ou complexe n’est pas réalisable avec les autres méthodes (créer ou effacer une table…).
Sécuriser ses requêtes
Avant d’aborder tout autre sujet, parlons rapidement de la sécurité. Dans la mesure où nous écrivons ici directement des requêtes MySQL, nos requêtes sont exécutées telles-quelles. Et si des données non gérées par nous y trouvent leur chemin, nous sommes en présence d’une faille de sécurité : l’injection SQL.
Comme en PHP avec PDO, nous avons une méthode prepare
. Celle de WordPress, pour des raisons historiques, fonctionne cependant différemment, elle se comporte comme sprintf
.
Nous avons trois placeholders selon le type de valeur :
%s
pour les chaines%d
pour les entiers%f
pour les décimaux
$escaped_sql = $wpdb->prepare("SELECT id, name FROM {$wpdb->users} WHERE user_email = %s", $email);
// Il est également possible d'utiliser une syntaxe à la vsprintf
$escaped_sql = $wpdb->prepare("SELECT id, name FROM {$wpdb->users} WHERE user_email = %s", [$email]);
// On exécute ensuite normalement la requête préparée
$results = $wpdb->get_results($escaped_sql);
Les deux méthodes acceptent bien entendu optionnellement plusieurs paramètres : séparés par des virgules pour la première, tableau pour la seconde.
Il n’est pas nécessaire d’utiliser prepare
avec toutes les valeurs. $wpdb->insert
et $wpdb->update
par exemple se chargent d’échapper les valeurs. Attention, les conditions ne sont pas échappées !
La documentation de chacune d’elle mentionne si une valeur doit être échappée ou non.
Commentaires
Rejoignez la discussion !