Warum wird query_posts () nicht als veraltet markiert?

15

query_posts()Technisch gesehen gibt es zwei Funktionen. Einer query_posts()ist tatsächlich WP_Query::query_posts()und der andere ist im globalen Raum.

Fragen aus der Vernunft:

Wenn global query_posts()ist das "böse", warum wird nicht veraltet?

Oder warum ist nicht markiert als _doing_it_wong.

prosti
quelle
2
Das ist eine gute Frage! Für andere, die darauf stoßen und nicht wissen, warum Sie query_posts () nicht verwenden sollten, gibt es hier und hier einige gute Fragen und Antworten .
Tim Malone

Antworten:

11

Wesentliche Frage

Lassen Sie uns graben sich in das Trio: ::query_posts, ::get_postsund class WP_Queryzu verstehen , ::query_postsbesser.

Der Grundstein für das Abrufen der Daten in WordPress ist die WP_QueryKlasse. Beide Methoden ::query_postsund ::get_postsverwenden diese Klasse.

Beachten Sie, dass die Klasse WP_Queryauch die Methoden mit demselben Namen enthält: WP_Query::query_postsund WP_Query::get_posts, aber wir berücksichtigen eigentlich nur die globalen Methoden, damit Sie nicht verwirrt werden.

Bildbeschreibung hier eingeben

Das verstehen WP_Query

Die Klasse mit dem Namen WP_Querywurde bereits 2004 eingeführt. Alle Felder mit der Markierung ☂ (Regenschirm) waren bereits 2004 vorhanden. Die zusätzlichen Felder wurden später hinzugefügt.

Hier ist die WP_QueryStruktur:

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 ist das Schweizer Taschenmesser.

Einige Dinge über WP_Query:

  • Dies können Sie über Argumente steuern, die Sie übergeben
  • es ist standardmäßig gierig
  • es enthält die Substanz zum Schleifen
  • es wird im globalen Raum x2 gespeichert
  • es kann primär oder sekundär sein
  • Es werden Hilfsklassen verwendet
  • Es hat einen praktischen pre_get_postsHaken
  • Es unterstützt sogar verschachtelte Schleifen
  • Es enthält die SQL-Abfragezeichenfolge
  • Es enthält die Anzahl der Ergebnisse
  • es hält die Ergebnisse
  • Es enthält die Liste aller möglichen Abfrageargumente
  • Es enthält die Vorlagenflags
  • ...

Ich kann das alles nicht erklären, aber einige davon sind schwierig, also geben wir kurze Tipps.

WP_Query können Sie über Argumente steuern, die Sie übergeben

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

Diese Liste von WordPress Version 4.7 wird sich in Zukunft sicherlich ändern.

Dies wäre das minimale Beispiel, bei dem das WP_QueryObjekt aus den Argumenten erstellt wird:

// 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 ist gierig

get all you canEntwickelt auf der Idee, WordPress-Entwickler beschlossen, alle möglichen Daten frühzeitig zu erhalten, da dies gut für die Leistung ist . Wenn die Abfrage 10 Posts aus der Datenbank entnimmt, werden daher standardmäßig auch die Begriffe und die Metadaten für diese Posts über separate Abfragen abgerufen. Begriffe und Metadaten werden zwischengespeichert (Prefetch).

Beachten Sie, dass das Zwischenspeichern nur für die Lebensdauer einer einzelnen Anforderung gilt.

Sie können die Caching deaktivieren , wenn Sie festgelegt update_post_meta_cacheund update_post_term_cachezu , falsewährend die Einstellung WP_QueryArgumente. Wenn die Zwischenspeicherung deaktiviert ist, werden die Daten nur bei Bedarf aus der Datenbank angefordert.

Bei den meisten WordPress-Blogs funktioniert das Zwischenspeichern gut, aber es kann vorkommen, dass Sie das Zwischenspeichern deaktivieren.

WP_Query verwendet Hilfsklassen

Wenn Sie dort WP_QueryFelder markiert haben, haben Sie diese drei:

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

Sie können sich vorstellen, in Zukunft neue hinzuzufügen.

Bildbeschreibung hier eingeben

WP_Query Enthält die Substanz zum Looping

In diesem Code:

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

Möglicherweise stellen Sie fest, dass WP_Querydie Substanz, die Sie iterieren können, vorhanden ist. Die Hilfsmethoden gibt es auch. Sie stellen gerade die whileSchleife ein.

Hinweis. forund whileSchleifen sind semantisch äquivalent.

WP_Query primär und sekundär

In WordPress haben Sie eine primäre und keine oder mehrere sekundäre Abfragen.

Es ist möglich, dass die primäre Abfrage nicht vorhanden ist, dies würde jedoch den Rahmen dieses Artikels sprengen.

Primäre Abfrage, die als Hauptabfrage oder reguläre Abfrage bezeichnet wird . Sekundäre Abfrage wird auch als benutzerdefinierte Abfrage bezeichnet .

WordPress verwendet WP_RewriteClass Early, um die Abfrageargumente basierend auf der URL zu erstellen. Basierend auf diesen Argumenten speichert es die zwei identischen Objekte im globalen Raum. Beide enthalten die Hauptabfrage.

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

Wenn wir main query sagen , denken wir an diese Variablen. Andere Abfragen können als sekundär oder benutzerdefiniert bezeichnet werden.

Es ist völlig legal, entweder global $wp_queryoder zu verwenden $GLOBALS['wp_query'], aber die Verwendung der zweiten Notation ist viel bemerkenswerter und erspart das Eingeben einer zusätzlichen Zeile innerhalb des Funktionsumfangs.

$GLOBALS['wp_query']und $GLOBALS['wp_the_query']sind separate Objekte. $GLOBALS['wp_the_query']sollte eingefroren bleiben.

WP_Queryhat den praktischen pre_get_postsHaken.

Dies ist der Aktionshaken. Es gilt für jede WP_Query Instanz. Du nennst es so:

add_action( 'pre_get_posts', function($query){

 if ( is_category() && $query->is_main_query() ) {
    // set your improved arguments
    $query->set( ... );  
    ...  
 }

 return $query;  
});

Dieser Hook ist großartig und kann beliebige Abfrageargumente ändern.

Folgendes können Sie lesen :

Wird ausgelöst, nachdem das Abfragevariablenobjekt erstellt wurde, aber bevor die eigentliche Abfrage ausgeführt wird.

Dieser Hook ist also Argument-Manager, kann jedoch keine neuen WP_QueryObjekte erstellen . Wenn Sie eine primäre und eine sekundäre Abfrage hatten, pre_get_postskönnen Sie die dritte nicht erstellen. Oder wenn Sie nur eine primäre hatten, kann sie die sekundäre nicht erstellen.

Beachten Sie, dass Sie den requestHook auch verwenden können, falls Sie nur die Hauptabfrage ändern müssen .

WP_Query unterstützt verschachtelte Schleifen

Dieses Szenario kann auftreten, wenn Sie Plugins verwenden und Plugin-Funktionen aus der Vorlage aufrufen.

Hier ist das Vorzeigebeispiel, in dem WordPress auch für verschachtelte Schleifen Hilfsfunktionen bietet:

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;

Die Ausgabe sieht folgendermaßen aus, da ich die Testdaten für die Themeneinheit installiert habe :

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!

Obwohl ich 5 Posts in der benutzerdefinierten $ query angefordert habe, werden mir sechs zurückgegeben, da der klebrige Post mitgeht. Wenn es wp_reset_postdataim vorherigen Beispiel keine gibt, ist die Ausgabe wie folgt, da $GLOBALS['post']sie ungültig ist.

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_Queryhat wp_reset_queryFunktion

Dies ist wie eine Reset-Taste. $GLOBALS['wp_the_query']sollte die ganze Zeit eingefroren sein und Plugins oder Themes sollten es niemals ändern.

Hier ist was zu wp_reset_querytun:

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

Bemerkungen zu get_posts

get_posts sieht aus wie

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);

Die Zeilennummern können sich in Zukunft ändern.

Es ist nur ein Wrapper um , WP_Querydass kehrt die Abfrageobjekt Beiträge.

Der ignore_sticky_postsWert true bedeutet, dass die klebrigen Pfosten möglicherweise nur in einer natürlichen Position angezeigt werden. Es gibt keine klebrigen Pfosten in der Front. Die andere no_found_rowsEinstellung auf true bedeutet, dass die WordPress-Datenbank-API keine SQL_CALC_FOUND_ROWSPaginierung implementiert und die Datenbank weniger belastet, um die Anzahl der gefundenen Zeilen auszuführen .

Dies ist praktisch, wenn Sie keine Paginierung benötigen. Wir verstehen jetzt, dass wir diese Funktion mit dieser Abfrage nachahmen können:

$args = array ( 'ignore_sticky_posts' => true, 'no_found_rows' => true);
$query = new WP_Query( $args );
print( $query->request );

Hier ist die entsprechende SQL-Anfrage:

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

Vergleichen Sie das, was wir jetzt haben, mit der vorherigen SQL-Anforderung, sofern SQL_CALC_FOUND_ROWSvorhanden.

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

Die Anfrage ohne SQL_CALC_FOUND_ROWSwird schneller sein.

Bemerkungen zu query_posts

Tipp: Anfangs gab es 2004 nur global $wp_query. Ab WordPress 2.1 $wp_the_querykam die Version . Tipp: $GLOBALS['wp_query']und $GLOBALS['wp_the_query']sind separate Objekte.

query_posts()ist WP_QueryWrapper. Es gibt die Referenz auf das WP_QueryHauptobjekt zurück und setzt gleichzeitig die global $wp_query.

File: /wp-includes/query.php
function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

In PHP4 wurde alles, einschließlich der Objekte, als Wert übergeben. query_postswar wie folgt:

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);
}

Bitte beachten Sie, dass in einem typischen Szenario mit einer primären und einer sekundären Abfrage diese drei Variablen vorliegen:

$GLOBALS['wp_the_query'] 
$GLOBALS['wp_query'] // should be the copy of first one
$custom_query // secondary

Angenommen, jeder dieser drei Speicher benötigt 1 MB Speicher. Insgesamt wären 3M Speicher. Wenn wir verwenden query_posts, $GLOBALS['wp_query']wird nicht gesetzt und neu erstellt.

PHP5 + sollte intelligent sein, um das $GLOBALS['wp_query']Objekt zu leeren, genau wie in PHP4, in dem wir es gemacht habenunset($GLOBALS['wp_query']);

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

Infolgedessen werden query_postsinsgesamt 2 MB Arbeitsspeicher und get_posts3 MB Arbeitsspeicher verbraucht.

Beachten Sie, dass query_postswir nicht das eigentliche Objekt zurücksenden, sondern einen Verweis auf das Objekt.

Von php.net : Eine PHP-Referenz ist ein Alias, mit dem zwei verschiedene Variablen auf denselben Wert schreiben können. Ab PHP 5 enthält eine Objektvariable das Objekt selbst nicht mehr als Wert. Es enthält nur eine Objektkennung, mit der Objektzugriffsberechtigte das tatsächliche Objekt finden können. Wenn ein Objekt als Argument gesendet, zurückgegeben oder einer anderen Variablen zugewiesen wird, sind die verschiedenen Variablen keine Aliase: Sie enthalten eine Kopie des Bezeichners, der auf dasselbe Objekt verweist.

Auch in PHP5 + ist der Assign (=) - Operator klug. Es werden flache Kopien und keine Ausdrucke verwendet. Wenn wir so schreiben, werden $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];nur die Daten kopiert, nicht das gesamte Objekt, da diese den gleichen Objekttyp haben.

Hier ist ein Beispiel

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'] ) ) );

Wird ergeben:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Versuchen Sie die Abfrage zurückzusetzen:

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'] ) ) );

Wird ergeben:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Sie können Probleme erstellen, auch wenn Sie verwenden 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'] ) ) );

Die Lösung wäre natürlich, die wp_reset_queryFunktion wieder zu verwenden.

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'] ) ) );

Dies ist der Grund, warum ich denke, query_postsdass es vom Standpunkt des Gedächtnisses aus vielleicht besser ist. Aber du solltest immer einen wp_reset_queryTrick machen.

prosti
quelle
10

Ich habe gerade ein neues Trac-Ticket, Ticket # 36874 , erstellt, um die Verfallserklärung von vorzuschlagen query_posts(). Ob es akzeptiert wird oder nicht, bleibt eine gute Frage.

Das wirklich große Problem dabei query_posts()ist, dass es immer noch von Plugins und Themes verwendet wird, obwohl es wirklich gute Schriften zum Thema gibt, warum Sie es NIEMALS verwenden sollten. Ich denke, der epischste Beitrag hier auf WPSE ist der folgende:

deprecation! == das Entfernen , so query_posts()dass die Verwendung durch Entwickler mit schlechter Qualität und Leute im Allgemeinen, die WordPress nicht kennen und Tutorials mit schlechter Qualität als Richtlinien verwenden, nicht aufhören wird, dies zu tun. So wie einige Beweise, wie viele Fragen bekommen wir immer noch hier , wo Menschen nutzen caller_get_postsin WP_Query? Es ist seit vielen Jahren veraltet.

Veraltete Funktionen und Argumente können jedoch jederzeit entfernt werden, wenn die Kernentwickler dies für richtig halten. Dies wird jedoch höchstwahrscheinlich niemals der Fall sein, query_posts()da dies Millionen von Websites zum Erliegen bringt. Also ja, wir werden wahrscheinlich nie das vollständige Entfernen von sehen query_posts()- was dazu führen könnte, dass es höchstwahrscheinlich niemals veraltet wird.

Dies ist zwar ein Ansatzpunkt, aber man muss bedenken, dass das Abwerten von WordPress-Inhalten die Verwendung nicht einschränkt.

UPDATE 19. Mai 2016

Das Ticket, das ich ausgelöst habe, ist jetzt geschlossen und als Duplikat eines 4 Jahre alten Tickets markiert , das als Wontfix geschlossen und wieder geöffnet wurde und weiterhin offen und ungelöst bleibt.

Die Kernentwickler scheinen an diesem alten treuen kleinen Übel festzuhalten. Alle Interessierten, hier ist das Duplikat des 4 Jahre alten Tickets

Pieter Goosen
quelle
Warum haben sie das Ticket core.trac.wordpress.org/ticket/36874 geschlossen ? Bitte @PieterGoosen können Sie den Link zu diesem Thread in Ihr Ticket core.trac.wordpress.org/ticket/36874 aufnehmen, da sich diese Frage auf das Ticket 1: 1
Prosti
@prosti Sieht so aus, als wäre es als Duplikat markiert worden, da dieses Problem bereits angesprochen wurde ... vor 4 Jahren hier gefunden .
Howdy_McGee
3

[etwas schimpfen]

Es ist an dieser Stelle die ständige Kernphilosophie, dass nichts wirklich veraltet ist. Auch wenn es eine nette Nachricht ist, wird sie einfach ignoriert, wenn die Funktion nicht tatsächlich irgendwann gelöscht wird. Es gibt viele Leute, die sich nicht mit entwickeln WP_DEBUGund die Meldung nicht bemerken, wenn es nicht zu einem tatsächlichen Bruch kommt.

OTOH Hand, diese Funktion ist wie eine gotoAussage. Persönlich habe ich nie (für eine kleinere Definition als erwartet) verwendet, gotoaber ich kann die Argumente verstehen, die auf eine Situation hinweisen, in der es standardmäßig nicht böse ist. Ebenso query_postsist es eine einfache Möglichkeit, alle für eine einfache Schleife erforderlichen Globals einzurichten, und kann im Ajax- oder Rest-Api-Kontext nützlich sein. Ich würde es niemals auch in solchen Kontexten verwenden, aber ich kann sehen, dass es sich eher um eine Frage des Codierungsstils als um eine Funktion handelt, die von sich aus böse ist.

Das Hauptproblem besteht darin, dass überhaupt Globals festgelegt werden müssen. Das ist das Hauptproblem, nicht die einzige Funktion, die beim Einstellen hilft.

Mark Kaplun
quelle
Und für den Vergleich ist das wirklich query_postslangsamer als eine sekundäre Abfrage (sprich: nicht die Hauptabfrage).
Prosti
@prosti, da es nur eine wp_query setzt und ausführt, wie viel langsamer kann es sein? Sicher, es ist etwas Overhead, aber wir reden hier wahrscheinlich über Millisekunden. Dies setzt natürlich voraus, dass Sie es an Orten verwenden, an denen WP standardmäßig keine Abfrage bereitstellt. An Orten, an denen es funktioniert, ist es schlecht, nicht das query_postsselbst, sondern die nutzlose Abfrage, die beim Laden von WP ausgeführt wurde
Mark Kaplun,