Pourquoi query_posts () n'est pas marqué comme obsolète?

15

Il y a deux query_posts()fonctions techniquement parlant. L'un query_posts()est en fait WP_Query::query_posts()et l'autre est dans l'espace mondial.

Demande de raison:

Si global query_posts()est ce "mal", pourquoi n'est-il pas déconseillé?

Ou pourquoi n'est pas marqué comme _doing_it_wong.

prosti
la source
2
Voilà une excellente question! Pour ceux qui rencontrent cela et qui ne savent pas pourquoi vous ne devriez pas utiliser query_posts (), voici et ici quelques bonnes questions-réponses sur le sujet.
Tim Malone

Réponses:

11

Question essentielle

La fouille Let dans le trio: ::query_posts, ::get_postset class WP_Queryde comprendre ::query_postsmieux.

La pierre angulaire pour obtenir les données dans WordPress est la WP_Queryclasse. Les deux méthodes ::query_postset ::get_postsutilisent cette classe.

Notez que la classe WP_Querycontient également les méthodes du même nom: WP_Query::query_postset WP_Query::get_posts, mais nous ne considérons en fait que les méthodes globales, alors ne vous trompez pas.

entrez la description de l'image ici

Comprendre le WP_Query

La classe appelée WP_Querya é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 WP_Querystructure:

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:

  • c'est quelque chose que vous pouvez contrôler via les arguments que vous passez
  • c'est gourmand par défaut
  • il contient la substance pour boucler
  • il est enregistré dans l'espace global x2
  • il peut être primaire ou secondaire
  • il utilise des classes d'assistance
  • il a un pre_get_postscrochet pratique
  • il prend même en charge les boucles imbriquées
  • il contient la chaîne de requête SQL
  • il contient le nombre de résultats
  • il détient les résultats
  • il contient la liste de tous les arguments de requête possibles
  • il contient les drapeaux de modèle
  • ...

Je ne peux pas expliquer tout cela, mais certains d'entre eux sont délicats, alors fournissons de courts conseils.

WP_Query est quelque chose que vous pouvez contrôler via les arguments que vous passez

The 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 va certainement changer à l'avenir.

Ce serait l'exemple minimal de création de l' WP_Queryobjet à 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 gourmand

Créés sur l'idée, les get all you candéveloppeurs WordPress ont décidé d'obtenir toutes les données possibles le plus tôt possible, car cela est bon pour les performances . C'est pourquoi par défaut, lorsque la requête prend 10 articles de la base de données, elle obtiendra également les termes et les métadonnées de ces articles via des requêtes distinctes. Les termes et métadonnées seront mis en cache (prélecture).

Notez que la mise en cache est juste pour la durée de vie d'une seule demande.

Vous pouvez désactiver la mise en cache si vous définissez update_post_meta_cacheet update_post_term_cachesur falselors de la définition des WP_Queryarguments. 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 il peut arriver que vous désactiviez la mise en cache.

WP_Query utilise des classes d'assistance

Si vous avez coché les WP_Querychamps, vous avez les trois suivants:

public $tax_query;
public $meta_query;
public $date_query;

Vous pouvez imaginer en ajouter de nouveaux à l'avenir.

entrez la description de l'image ici

WP_Query détient la substance pour boucler

Dans ce code:

$query = new WP_Query( $args )
if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();

vous remarquerez peut-être la WP_Querysubstance que vous pouvez parcourir. Les méthodes d'assistance existent également. Vous venez de définir la whileboucle.

Remarque. foret les whileboucles sont sémantiquement équivalentes.

WP_Query primaire et secondaire

Dans WordPress, vous avez une requête principale et zéro ou plusieurs requêtes secondaires .

Il est possible de ne pas avoir la requête principale, mais cela dépasse le cadre de cet article.

Requête principale connue sous le nom de requête principale ou requête régulière . La requête secondaire est également appelée requête personnalisée .

WordPress utilise la WP_Rewriteclasse tôt 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 contiendront la requête principale.

global $wp_query   @since WordPress 1.5
global $wp_the_query @since WordPress 2.1

Lorsque 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_queryou $GLOBALS['wp_query'], mais l'utilisation de la deuxième notation est beaucoup plus notable et évite de taper une ligne supplémentaire à l'intérieur de la portée des fonctions.

$GLOBALS['wp_query']et $GLOBALS['wp_the_query']sont des objets séparés. $GLOBALS['wp_the_query']devrait rester gelé.

WP_Querya le pre_get_postscrochet pratique .

Ceci est le crochet d'action. Il s'appliquera à toute WP_Query instance. 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 crochet est génial et il peut modifier tous les arguments de requête.

Voici ce que vous pouvez lire :

Se déclenche après la création de l'objet variable de requête, mais avant l'exécution de la requête réelle.

Ce crochet est donc le gestionnaire d'arguments mais ne peut pas créer de nouveaux WP_Queryobjets. Si vous aviez une requête principale et une requête secondaire, pre_get_postsvous ne pouvez pas créer la troisième. Ou si vous venez d'avoir un primaire, il ne peut pas créer le secondaire.

Notez que si vous devez modifier la requête principale, vous pouvez également utiliser le requesthook.

WP_Query prend en charge les boucles imbriquées

Ce scénario peut se produire si vous utilisez des plugins et que vous appelez des fonctions de plugin à partir du modèle.

Voici l'exemple de vitrine WordPress a 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 ceci depuis que j'ai installé les 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 $ personnalisée, il m'en retournera six, car le message collant ira de pair. S'il n'y en a pas wp_reset_postdatadans l'exemple précédent, la sortie sera comme ceci, car elle $GLOBALS['post']ne sera pas valide.

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_Querya une wp_reset_queryfonction

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_queryfont:

function wp_reset_query() {
    $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
    wp_reset_postdata();
}

Remarques sur 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 à l'avenir.

Il est juste un wrapper autour WP_Queryque le rendement des messages de l' objet de la requête.

La valeur ignore_sticky_poststrue signifie que les poteaux collants peuvent apparaître uniquement dans une position naturelle. Il n'y aura pas de poteaux collants à l'avant. L'autre valeur no_found_rowstrue signifie que l'API de base de données WordPress ne sera pas utilisée SQL_CALC_FOUND_ROWSpour implémenter la pagination, réduisant ainsi la charge sur la base de données pour exécuter le nombre de lignes trouvées .

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ù elle SQL_CALC_FOUND_ROWSexiste.

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_ROWSsera plus rapide.

Remarques sur query_posts

Astuce: au début en 2004, il y en avait seulement global $wp_query. À partir de la version WordPress 2.1 est $wp_the_queryarrivée. Astuce: $GLOBALS['wp_query']et $GLOBALS['wp_the_query']sont des objets séparés.

query_posts()est WP_Querywrapper. Il renvoie la référence à l' WP_Queryobjet principal et en même temps, il définit 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 ça:

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. Le total serait de 3 m de mémoire. Si nous utilisons query_posts, $GLOBALS['wp_query']sera non défini et créé à nouveau.

PHP5 + devrait être intelligent en vidant l' $GLOBALS['wp_query']objet, tout comme en PHP4 nous l'avons fait avec leunset($GLOBALS['wp_query']);

function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Par conséquent, query_postsconsomme 2M de mémoire au total, tandis que get_postsconsomme 3 M de mémoire.

Notez que query_postsnous ne retournons 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 sur la même valeur. Depuis PHP 5, une variable objet ne contient plus l'objet lui-même comme valeur. Il contient uniquement un identifiant d'objet qui permet aux accesseurs d'objet de trouver l'objet réel. Lorsqu'un objet est envoyé par argument, retourné 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 une copie superficielle 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 ceux-ci 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 de réutiliser la wp_reset_queryfonction.

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 cela query_postspeut être mieux du point de vue de la mémoire. Mais vous devriez toujours faire des wp_reset_querytours.

prosti
la source
10

Je viens de créer un nouveau ticket trac, ticket # 36874 , pour proposer la dépréciation de query_posts(). Que ce soit ou non accepté reste une bonne question.

Le vrai gros problème avec query_posts(), c'est qu'il est encore largement utilisé par les plugins et les thèmes, même s'il y a eu de très bons écrits sur le pourquoi de JAMAIS JAMAIS l' utiliser. Je pense que le post le plus épique ici sur WPSE est le suivant:

suppression de deprecation! == , donc obsolètequery_posts() n'empêchera 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 lignes directrices. Tout comme une preuve, combien de questions recevons-nous encore là où les gens utilisentcaller_get_posts dans WP_Query? Il est obsolète depuis de nombreuses années maintenant.

Les fonctions et arguments obsolètes peuvent cependant être supprimés à tout moment que les développeurs principaux jugent opportun, mais cela ne se produira probablement jamais avec query_posts() car cela entraînera la rupture de millions de sites. Alors oui, nous ne verrons probablement jamais la suppression totale de query_posts()- ce qui pourrait conduire au fait qu'il ne sera probablement jamais obsolète.

C'est un point de départ, mais il faut se rappeler que la dépréciation de quelque chose dans WordPress n'arrête pas son utilisation.

MISE À JOUR 19 mai 2016

Le ticket que j'ai soulevé est maintenant fermé et marqué comme dupliqué sur un ticket de 4 ans , qui a été fermé comme wontfix et a été rouvert et reste ouvert et non résolu.

Il semble que les développeurs principaux s'accrochent à ce vieux petit mal fidèle. Tout le monde est intéressé, voici le ticket de 4 ans en double

Pieter Goosen
la source
Pourquoi ils ont fermé le ticket core.trac.wordpress.org/ticket/36874 ? Veuillez @PieterGoosen pouvez-vous inclure le lien vers ce fil dans votre ticket core.trac.wordpress.org/ticket/36874 car cette question concerne le ticket 1: 1
prosti
@prosti On dirait qu'il a été marqué comme doublon car ce problème a déjà été signalé ... il y a 4 ans trouvé ici .
Howdy_McGee
3

[un peu coup de gueule]

C'est la philosophie de base à ce stade que rien n'est vraiment déconseillé. L'avis de dépréciation, bien qu'il soit agréable à avoir, sera simplement ignoré si la fonction ne sera pas supprimée à un moment donné. Il y a beaucoup de gens qui ne se développent pas avec WP_DEBUGet ne remarqueront pas l'avis s'il n'y aura pas de rupture réelle.

OTOH part, cette fonction est comme une gotodéclaration. Personnellement, je n'ai jamais (pour une définition plus petite que prévu) utiliségoto mais je peux comprendre les arguments pointant vers une situation dans laquelle ce n'est pas mal par défaut. Il en va de mêmequery_posts , c'est un moyen simple de configurer tous les globaux requis pour faire une boucle simple, et peut être utile dans le contexte ajax ou rest-api. Je ne l'emploierais jamais dans ces contextes aussi, mais je peux voir que là, c'est plus une question de style de codage qu'une fonction étant mauvaise en soi.

En allant un peu plus loin, le principal problème est que les globaux doivent être définis. C'est le problème principal, pas la seule fonction qui aide à les définir.

Mark Kaplun
la source
Et pour la comparaison, c'est vraiment query_postsplus lent qu'une requête secondaire (lire: pas la requête principale).
prosti
@prosti, car il définit et exécute simplement un wp_query, combien peut-il être plus lent? Bien sûr, il y a des frais généraux, mais nous parlons probablement de millisecondes ici. Cela suppose bien sûr que vous l'utilisiez dans des endroits où WP ne fournit pas de requête par défaut. Dans les endroits où il le fait, c'est mauvais, pas le query_postslui - même mais la requête inutile qui a été faite lors du chargement de WP
Mark Kaplun