Techniquement, il existe deux fonctions query_posts()
. Une query_posts()
est en réalité WP_Query::query_posts()
et l'autre est dans l'espace global.
Demander à la santé mentale:
Si query_posts()
global est ce "mal", pourquoi n'est-il pas obsolète?
Ou pourquoi n'est pas marqué comme _doing_it_wong
.
Fouillons dans le trio: ::query_posts
, ::get_posts
et class WP_Query
pour mieux comprendre ::query_posts
.
La pierre angulaire pour obtenir les données dans WordPress est la classe WP_Query
. Les deux méthodes ::query_posts
et ::get_posts
utilisent cette classe.
Notez que la classe
WP_Query
contient également les méthodes du même nom:WP_Query::query_posts
etWP_Query::get_posts
, mais nous ne considérons en fait que les méthodes globales, ne vous trompez donc pas.
WP_Query
La classe appelée
WP_Query
a été introduite en 2004. Tous les champs portant la marque (parapluie) étaient présents en 2004. Les champs supplémentaires ont été ajoutés ultérieurement.
Voici la structure WP_Query
:
class WP_Query (as in WordPress v4.7)
public $query; ☂
public $query_vars = array(); ☂
public $tax_query;
public $meta_query = false;
public $date_query = false;
public $queried_object; ☂
public $queried_object_id; ☂
public $request;
public $posts; ☂
public $post_count = 0; ☂
public $current_post = -1; ☂
public $in_the_loop = false;
public $post; ☂
public $comments;
public $comment_count = 0;
public $current_comment = -1;
public $comment;
public $found_posts = 0;
public $max_num_pages = 0;
public $max_num_comment_pages = 0;
public $is_single = false; ☂
public $is_preview = false; ☂
public $is_page = false; ☂
public $is_archive = false; ☂
public $is_date = false; ☂
public $is_year = false; ☂
public $is_month = false; ☂
public $is_day = false; ☂
public $is_time = false; ☂
public $is_author = false; ☂
public $is_category = false; ☂
public $is_tag = false;
public $is_tax = false;
public $is_search = false; ☂
public $is_feed = false; ☂
public $is_comment_feed = false;
public $is_trackback = false; ☂
public $is_home = false; ☂
public $is_404 = false; ☂
public $is_embed = false;
public $is_paged = false;
public $is_admin = false; ☂
public $is_attachment = false;
public $is_singular = false;
public $is_robots = false;
public $is_posts_page = false;
public $is_post_type_archive = false;
private $query_vars_hash = false;
private $query_vars_changed = true;
public $thumbnails_cached = false;
private $stopwords;
private $compat_fields = array('query_vars_hash', 'query_vars_changed');
private $compat_methods = array('init_query_flags', 'parse_tax_query');
private function init_query_flags()
WP_Query
est le couteau suisse.Quelques points sur WP_Query
:
pre_get_posts
Je ne peux pas expliquer tout cela, mais certains d'entre eux sont difficiles, donnons-en de brefs conseils.
WP_Query
est quelque chose que vous pouvez contrôler via les arguments que vous passezThe list of the arguments
---
attachment
attachment_id
author
author__in
author__not_in
author_name
cache_results
cat
category__and
category__in
category__not_in
category_name
comments_per_page
day
embed
error
feed
fields
hour
ignore_sticky_posts
lazy_load_term_meta
m
menu_order
meta_key
meta_value
minute
monthnum
name
no_found_rows
nopaging
order
p
page_id
paged
pagename
post__in
post__not_in
post_name__in
post_parent
post_parent__in
post_parent__not_in
post_type
posts_per_page
preview
s
second
sentence
static
subpost
subpost_id
suppress_filters
tag
tag__and
tag__in
tag__not_in
tag_id
tag_slug__and
tag_slug__in
tb
title
update_post_meta_cache
update_post_term_cache
w
year
Cette liste de WordPress version 4.7 changera certainement dans le futur.
Ce serait l'exemple minimal créant l'objet WP_Query
à partir des arguments:
// WP_Query arguments
$args = array ( /* arguments*/ );
// creating the WP_Query object
$query = new WP_Query( $args );
// print full list of arguments WP_Query can take
print ( $query->query_vars );
WP_Query
est gourmandCréé sur l’idée get all you can
, les développeurs WordPress ont décidé d’obtenir rapidement toutes les données possibles car c’est bon pour la performance . C'est pourquoi, par défaut, lorsque la requête prélève 10 publications dans la base de données, les termes et les métadonnées de ces publications sont également collectés via des requêtes distinctes. Les termes et métadonnées seront mis en cache (pré-extraits).
Notez que la mise en cache ne concerne que la durée de vie d'une seule demande.
Vous pouvez désactiver la mise en cache si vous définissez update_post_meta_cache
et update_post_term_cache
sur false
tout en définissant les arguments WP_Query
. Lorsque la mise en cache est désactivée, les données ne seront demandées à la base de données que sur demande.
Pour la majorité des blogs WordPress, la mise en cache fonctionne bien, mais vous pouvez parfois désactiver la mise en cache.
WP_Query
utilise des classes auxiliairesSi vous avez coché les champs WP_Query
, vous avez ces trois:
public $tax_query;
public $meta_query;
public $date_query;
Vous pouvez imaginer ajouter de nouveaux dans le futur.
WP_Query
contient la substance pour la mise en boucleDans ce code:
$query = new WP_Query( $args )
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
vous remarquerez peut-être que le WP_Query
contient la substance que vous pouvez itérer. Les méthodes d'assistance sont là aussi. Vous venez de définir la boucle while
.
Remarque. Les boucles
for
etwhile
sont sémantiquement équivalentes.
WP_Query
primaire et secondaireDans WordPress, vous avez une primaire et zéro ou plus secondaire requêtes.
Il est possible de ne pas avoir la requête principale, mais cela sort du cadre de cet article.
Requête primaire connue sous le nom de requête principale ou la requête standard. Requête secondaire également appelée requête personnalisée.
WordPress utilise la classe WP_Rewrite
à un stade précoce pour créer les arguments de requête basés sur l'URL. Sur la base de ces arguments, il stocke les deux objets identiques dans l'espace global. Les deux détiendront la requête principale.
global $wp_query @since WordPress 1.5
global $wp_the_query @since WordPress 2.1
Quand nous disons requête principale}, nous pensons à ces variables. D'autres requêtes peuvent être appelées secondaires ou personnalisées.
Il est tout à fait légal d’utiliser soit
global $wp_query
, soit$GLOBALS['wp_query']
, mais l’utilisation de la deuxième notation est beaucoup plus notable et permet d’enregistrer une ligne supplémentaire à l’intérieur de la portée des fonctions.
$GLOBALS['wp_query']
et$GLOBALS['wp_the_query']
sont des objets distincts.$GLOBALS['wp_the_query']
devrait rester figé.
WP_Query
a le crochet pratique pre_get_posts
.C'est le crochet d'action. Cela s'appliquera à toute instance WP_Query
. Vous l'appelez comme:
add_action( 'pre_get_posts', function($query){
if ( is_category() && $query->is_main_query() ) {
// set your improved arguments
$query->set( ... );
...
}
return $query;
});
Ce hook est génial et peut altérer tous les arguments de la requête.
Voici ce que vous pouvez lisez :
Se déclenche après la création de l'objet de variable de requête, mais avant l'exécution de la requête réelle.
Ce hook est donc un gestionnaire d'arguments mais ne peut pas créer de nouveaux objets WP_Query
. Si vous avez une requête principale et une requête secondaire, pre_get_posts
ne peut pas créer la troisième. Ou si vous avez juste un primaire, il ne peut pas créer le secondaire.
Remarque Si vous devez modifier la requête principale uniquement, vous pouvez également utiliser le hook
request
.
WP_Query
supporte les boucles imbriquéesCe scénario peut se produire si vous utilisez des plug-ins et que vous appelez des fonctions de plug-in à partir du modèle.
Voici l'exemple de démonstration WordPress possède des fonctions d'assistance, même pour les boucles imbriquées:
global $id;
while ( have_posts() ) : the_post();
// the custom $query
$query = new WP_Query( array( 'posts_per_page' => 5 ) );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) : $query->the_post();
echo '<li>Custom ' . $id . '. ' . get_the_title() . '</li>';
endwhile;
}
wp_reset_postdata();
echo '<li>Main Query ' . $id . '. ' . get_the_title() . '</li>';
endwhile;
La sortie sera comme ça depuis que j'ai installé données de test d'unité de thème :
Custom 100. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 1. Hello world!
Même si j'ai demandé 5 messages dans la requête $ $ custom, il m'en retournera six, car le message persistant suivra. S'il n'y a pas wp_reset_postdata
dans l'exemple précédent, la sortie sera comme ceci, car le $GLOBALS['post']
sera invalide.
Custom 1001. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 13. Markup: Title With Special Characters
WP_Query
a la fonction wp_reset_query
C'est comme un bouton de réinitialisation. $GLOBALS['wp_the_query']
devrait être gelé tout le temps, et les plugins ou les thèmes ne devraient jamais le modifier.
Voici ce que wp_reset_query
fait:
function wp_reset_query() {
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
wp_reset_postdata();
}
get_posts
get_posts
ressemble à
File: /wp-includes/post.php
1661: function get_posts( $args = null ) {
1662: $defaults = array(
1663: 'numberposts' => 5,
1664: 'category' => 0, 'orderby' => 'date',
1665: 'order' => 'DESC', 'include' => array(),
1666: 'exclude' => array(), 'meta_key' => '',
1667: 'meta_value' =>'', 'post_type' => 'post',
1668: 'suppress_filters' => true
1669: );
... // do some argument parsing
1685: $r['ignore_sticky_posts'] = true;
1686: $r['no_found_rows'] = true;
1687:
1688: $get_posts = new WP_Query;
1689: return $get_posts->query($r);
Les numéros de ligne peuvent changer dans le futur.
C'est juste un wrapper autour de WP_Query
que renvoie l'objet de la requête est publié.
Le ignore_sticky_posts
défini sur true signifie que les posts collants ne peuvent apparaître que dans une position naturelle. Il n'y aura pas de poteaux collants à l'avant. L'autre no_found_rows
défini sur true signifie que l'API de base de données WordPress n'utilisera pas SQL_CALC_FOUND_ROWS
afin d'implémenter la pagination, réduisant ainsi la charge sur la base de données pour exécuter lignes trouvées décompte.
C'est pratique lorsque vous n'avez pas besoin de pagination. Nous comprenons maintenant que nous pouvons imiter cette fonction avec cette requête:
$args = array ( 'ignore_sticky_posts' => true, 'no_found_rows' => true);
$query = new WP_Query( $args );
print( $query->request );
Voici la requête SQL correspondante:
SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10
Comparez ce que nous avons maintenant avec la requête SQL précédente où SQL_CALC_FOUND_ROWS
existe.
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10
La demande sans SQL_CALC_FOUND_ROWS
sera plus rapide.
query_posts
Conseil: en 2004, il n'y avait que
global $wp_query
. À partir de WordPress 2.1, la version$wp_the_query
est arrivée. Conseil:$GLOBALS['wp_query']
et$GLOBALS['wp_the_query']
sont des objets distincts.
query_posts()
est WP_Query
wrapper. Il renvoie la référence à l'objet WP_Query
principal et définit en même temps le global $wp_query
.
File: /wp-includes/query.php
function query_posts($args) {
$GLOBALS['wp_query'] = new WP_Query();
return $GLOBALS['wp_query']->query($args);
}
En PHP4, tout, y compris les objets, était passé par valeur. query_posts
était comme ceci:
File: /wp-includes/query.php (WordPress 3.1)
function &query_posts($args) {
unset($GLOBALS['wp_query']);
$GLOBALS['wp_query'] =& new WP_Query();
return $GLOBALS['wp_query']->query($args);
}
Veuillez noter que dans un scénario typique avec une requête principale et une requête secondaire, nous avons ces trois variables:
$GLOBALS['wp_the_query']
$GLOBALS['wp_query'] // should be the copy of first one
$custom_query // secondary
Disons que chacun de ces trois prend 1M de mémoire. Total serait 3M de mémoire. Si nous utilisons query_posts
, $GLOBALS['wp_query']
sera supprimé et créé à nouveau.
PHP5 + devrait être intelligent en vidant l'objet $GLOBALS['wp_query']
, tout comme en PHP4 nous l'avons fait avec la unset($GLOBALS['wp_query']);
function query_posts($args) {
$GLOBALS['wp_query'] = new WP_Query();
return $GLOBALS['wp_query']->query($args);
}
En conséquence, query_posts
consomme 2 M de mémoire au total, alors que get_posts
consomme 3 M de mémoire.
Remarque Dans query_posts
, nous ne renvoyons pas l'objet réel, mais une référence à l'objet.
De php.net : Une référence PHP est un alias, qui permet à deux variables différentes d'écrire à la même valeur. A partir de PHP 5, une variable d'objet ne contient plus l'objet lui-même en tant que valeur. Il ne contient qu'un identifiant d'objet qui permet aux accesseurs d'objet de trouver l'objet réel. Lorsqu'un objet est envoyé par argument, renvoyé ou affecté à une autre variable, les différentes variables ne sont pas des alias: elles contiennent une copie de l'identifiant qui pointe vers le même objet.
Également en PHP5 +, l'opérateur assign (=) est intelligent. Il utilisera shallow copy et non une copie d'objet dur. Lorsque nous écrivons comme ceci
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
, seules les données seront copiées, pas l'objet entier, car elles partagent le même type d'objet.
Voici un exemple
print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
Résultera:
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5
Essayez de réinitialiser la requête:
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
Résultera:
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
Vous pouvez créer des problèmes même si vous utilisez WP_Query
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
Bien sûr, la solution serait d’utiliser à nouveau la fonction wp_reset_query
.
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
C'est pourquoi je pense que query_posts
pourrait être meilleur du point de vue de la mémoire. Mais vous devriez toujours faire wp_reset_query
astuce.
Je viens de créer un nouveau ticket trac, ticket # 36874 , pour proposer la désapprobation de query_posts()
. Que cela soit accepté ou non reste une bonne question.
Le gros problème avec query_posts()
est qu’il est toujours largement utilisé par les plugins et les thèmes, même s’il existe de très bons écrits sur les raisons pour lesquelles vous devriez JAMAIS JAMAIS l’utiliser. Je pense que le post le plus épique sur WPSE est le suivant:
déprécie! == suppression}, déprécier query_posts()
n'arrêtera donc pas son utilisation par les développeurs de mauvaise qualité et les personnes en général qui ne connaissent pas WordPress et qui utilisent des tutoriels de mauvaise qualité comme directives. Pour preuve, combien de questions avons-nous encore ici où les gens utilisent caller_get_posts
dans WP_Query
? Il est obsolète depuis de nombreuses années maintenant.
Les fonctions et les arguments obsolètes peuvent toutefois être supprimés à tout moment si les développeurs le jugent bon, mais cela n'arrivera probablement jamais avec query_posts()
car cela endommagerait des millions de sites. Donc oui, nous ne verrons probablement jamais la suppression totale de query_posts()
- ce qui pourrait expliquer le fait qu'il ne sera probablement jamais obsolète.
Ceci est un point de départ cependant, mais il faut se rappeler, désapprouver quelque chose dans WordPress n'arrête pas son utilisation.
Le ticket que j'ai généré est maintenant fermé et marqué en double par unâgé de 4 ansticket, qui a été fermé en tant que wontfix et a été rouvert mais reste ouvert et non résolu. .
Il semble que les principaux développeurs s’accrochent à ce vieux petit fidèle fidèle. Tout le monde est intéressé, voici le ticket dupliqué de 4 ans
[un peu rant]
À ce stade, la philosophie de base est que rien n’est vraiment déprécié. La notification de dépréciation, bien que ce soit une bonne chose à faire, va simplement être ignorée si la fonction ne sera pas supprimée à un moment donné. Il y a beaucoup de gens qui ne développent pas avec WP_DEBUG
et ne remarqueront pas l'avis s'il n'y a pas de casse réelle.
OTOH hand, cette fonction est comme une instruction goto
. Personnellement, je n’ai jamais (pour une définition plus petite qu’attendue) utilisé goto
mais je peux comprendre les arguments qui indiquent une situation dans laquelle ce n’est pas mauvais par défaut. Même chose avec query_posts
, c'est un moyen simple de configurer tous les globaux nécessaires à la création d'une boucle simple, et peut être utile dans un contexte ajax ou rest-api. Je ne l'utiliserais jamais non plus dans ces contextes, mais je peux voir que là-bas, il s'agit davantage d'un problème de style de codage qu'une fonction maléfique en soi.
Pour aller un peu plus loin, le principal problème est que les globals doivent être définis. C’est le principal problème et non la seule fonction qui facilite leur réglage.