Dlaczego zapytanie_posts () nie jest oznaczone jako przestarzałe?

15

Istnieją dwie query_posts()funkcje technicznie rzecz biorąc. Jedno query_posts()jest faktycznie, WP_Query::query_posts()a drugie w globalnej przestrzeni.

Pytanie z rozsądku:

Jeśli globalne query_posts()jest to „zło”, dlaczego nie jest przestarzałe?

Lub dlaczego nie jest oznaczony jako _doing_it_wong.

prosti
źródło
2
To świetne pytanie! Dla innych, którzy natkną się na to, którzy nie wiedzą, dlaczego nie powinieneś używać query_posts (), tutaj i tutaj jest kilka dobrych pytań i odpowiedzi dotyczących primera.
Tim Malone

Odpowiedzi:

11

Istotne pytanie

Dig w trio Powiedzmy: ::query_posts, ::get_postsi class WP_Queryzrozumieć ::query_postslepiej.

Podstawą do uzyskania danych w WordPress jest WP_Queryklasa. Obie metody ::query_postsi ::get_postsużyj tej klasy.

Zauważ, że klasa WP_Queryzawiera również metody o tej samej nazwie: WP_Query::query_postsi WP_Query::get_posts, ale w rzeczywistości rozważamy tylko metody globalne, więc nie daj się zwieść.

wprowadź opis zdjęcia tutaj

Zrozumienie WP_Query

WP_QueryNazwana klasa została wprowadzona w 2004 roku. Wszystkie pola oznaczone znakiem umbrella (parasol) były obecne w 2004 roku. Dodatkowe pola zostały dodane później.

Oto WP_Querystruktura:

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 to szwajcarski scyzoryk.

Niektóre rzeczy na temat WP_Query:

  • można to kontrolować za pomocą przekazywanych argumentów
  • domyślnie jest chciwy
  • zawiera substancję do zapętlenia
  • jest zapisany w globalnej przestrzeni x2
  • może być pierwotny lub wtórny
  • wykorzystuje klasy pomocnicze
  • ma poręczny pre_get_postshaczyk
  • obsługuje nawet zagnieżdżone pętle
  • przechowuje ciąg zapytania SQL
  • zawiera liczbę wyników
  • trzyma wyniki
  • przechowuje listę wszystkich możliwych argumentów zapytania
  • zawiera flagi szablonu
  • ...

Nie potrafię tego wyjaśnić, ale niektóre z nich są trudne, więc podajmy krótkie wskazówki.

WP_Query możesz kontrolować za pomocą przekazywanych argumentów

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

Ta lista z WordPress w wersji 4.7 z pewnością zmieni się w przyszłości.

Byłby to minimalny przykład tworzenia WP_Queryobiektu na podstawie argumentów:

// 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 jest chciwy

Stworzony na podstawie pomysłu, że get all you canprogramiści WordPress postanowili uzyskać wszystkie możliwe dane wcześniej, ponieważ jest to dobre dla wydajności . Dlatego domyślnie, gdy zapytanie pobiera 10 postów z bazy danych, będzie również otrzymywać warunki i metadane dla tych postów za pośrednictwem oddzielnych zapytań. Warunki i metadane będą buforowane (wstępnie pobierane).

Uwaga: buforowanie dotyczy tylko jednego życia żądania.

Można wyłączyć buforowanie po ustawieniu update_post_meta_cachei update_post_term_cacheaby falsepodczas ustawiania WP_Queryargumenty. Gdy buforowanie jest wyłączone, dane będą pobierane z bazy danych tylko na żądanie.

W przypadku większości blogów WordPress buforowanie działa dobrze, ale w niektórych przypadkach możesz wyłączyć buforowanie.

WP_Query używa klas pomocniczych

Jeśli WP_Queryzaznaczyłeś tam pola, masz te trzy:

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

Możesz sobie wyobrazić dodawanie nowych w przyszłości.

wprowadź opis zdjęcia tutaj

WP_Query zatrzymuje substancję do zapętlenia

W tym kodzie:

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

możesz zauważyć, że WP_Queryma substancję, którą możesz iterować. Istnieją również metody pomocnicze. Właśnie ustawiłeś whilepętlę.

Uwaga. fora whilepętle są semantycznie równoważne.

WP_Query Pierwszy i drugi

W WordPress masz jedno podstawowe i zero lub więcej wtórnych zapytań.

Możliwe, że nie ma podstawowego zapytania, ale wykracza to poza zakres tego artykułu.

Zapytanie podstawowe znane jako zapytanie główne lub zapytanie zwykłe . Drugie zapytanie nazywane jest również niestandardowym .

WordPress używa WP_Rewriteklasy wcześnie, aby utworzyć argumenty zapytania na podstawie adresu URL. Na podstawie tych argumentów przechowuje dwa identyczne obiekty w przestrzeni globalnej. Oba będą zawierać główne zapytanie.

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

Kiedy mówimy główne zapytanie , myślimy o tych zmiennych. Inne zapytania można nazwać drugorzędnymi lub niestandardowymi.

Używanie jednego global $wp_querylub $GLOBALS['wp_query']drugiego jest całkowicie legalne , ale użycie drugiej notacji jest znacznie bardziej zauważalne i pozwala zaoszczędzić wpisywanie dodatkowej linii w zakresie funkcji.

$GLOBALS['wp_query']i $GLOBALS['wp_the_query']są osobnymi obiektami. $GLOBALS['wp_the_query']powinien pozostać zamrożony.

WP_Queryma poręczny pre_get_postshaczyk.

To jest hak akcji. Będzie miało zastosowanie do dowolnej WP_Query instancji. Nazywasz to tak:

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

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

 return $query;  
});

Ten haczyk jest świetny i może zmieniać dowolne argumenty zapytania.

Oto, co możesz przeczytać :

Uruchamia się po utworzeniu obiektu zmiennej zapytania, ale przed uruchomieniem rzeczywistego zapytania.

Ten hak jest menedżerem argumentów, ale nie może tworzyć nowych WP_Queryobiektów. Jeśli masz jedno zapytanie podstawowe i jedno dodatkowe, pre_get_postsnie możesz utworzyć trzeciego. Lub jeśli masz tylko jeden element główny, nie może utworzyć elementu dodatkowego.

Uwaga: w przypadku potrzeby zmiany głównego zapytania tylko Ty możesz użyć requesthaka.

WP_Query obsługuje zagnieżdżone pętle

Ten scenariusz może się zdarzyć, jeśli korzystasz z wtyczek i wywołujesz funkcje wtyczek z szablonu.

Oto przykładowy przykład WordPress ma funkcje pomocnicze nawet dla zagnieżdżonych pętli:

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;

Wynik będzie taki, ponieważ zainstalowałem dane testu jednostki tematycznej :

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!

Mimo że poprosiłem o 5 postów w niestandardowym zapytaniu $, zwróci mi sześć, ponieważ post przyklei się. Jeśli nie ma wp_reset_postdataw poprzednim przykładzie, wynik będzie taki, ponieważ $GLOBALS['post']będzie nieprawidłowy.

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_Queryma wp_reset_queryfunkcję

To jest jak przycisk resetowania. $GLOBALS['wp_the_query']powinien być cały czas zamrożony, a wtyczki lub motywy nigdy nie powinny go zmieniać.

Oto co wp_reset_queryrobią:

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

Uwagi na temat get_posts

get_posts wygląda jak

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

Numery linii mogą ulec zmianie w przyszłości.

To jest po prostu otoki wokół WP_Query, że wraca zapytaniu posty obiektu.

ignore_sticky_postsUstawiona na true oznacza lepkie wpisy mogą pojawić się tylko w naturalnej pozycji. Z przodu nie będzie lepkich słupków. Drugi no_found_rowszestaw wartości true oznacza, że ​​interfejs API bazy danych WordPress nie będzie używany SQL_CALC_FOUND_ROWSdo implementacji paginacji, co zmniejszy obciążenie bazy danych w celu wykonania liczby znalezionych wierszy .

Jest to przydatne, gdy nie potrzebujesz stronicowania. Rozumiemy teraz, że możemy naśladować tę funkcję za pomocą tego zapytania:

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

Oto odpowiednie zapytanie SQL:

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

Porównaj to, co mamy teraz, z poprzednim żądaniem SQL, o ile SQL_CALC_FOUND_ROWSistnieje.

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

Żądanie bez SQL_CALC_FOUND_ROWSbędzie szybsze.

Uwagi na temat query_posts

Wskazówka: na początku w 2004 roku było tylko global $wp_query. Od wersji WordPress 2.1 $wp_the_queryprzyszła. Wskazówka: $GLOBALS['wp_query']i $GLOBALS['wp_the_query']są osobnymi obiektami.

query_posts()jest WP_Queryopakowaniem. Zwraca referencję do głównego WP_Queryobiektu i jednocześnie ustawi global $wp_query.

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

W PHP4 wszystko, łącznie z obiektami, przechodziło przez wartość. query_postsbyło tak:

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

Uwaga: w typowym scenariuszu z jednym zapytaniem podstawowym i jednym zapytaniem dodatkowym mamy trzy zmienne:

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

Powiedzmy, że każda z tych trzech zajmuje 1M pamięci. Łącznie byłoby 3 mln pamięci. Jeśli użyjemy query_posts, $GLOBALS['wp_query']zostanie rozbrojony i ponownie utworzony.

PHP5 + powinno być inteligentnie opróżniające $GLOBALS['wp_query']obiekt, tak jak w PHP4 zrobiliśmy to zunset($GLOBALS['wp_query']);

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

W rezultacie query_postszużywa ogółem 2 mln pamięci, a get_postszużywa 3 mln pamięci.

Uwaga: query_postsnie zwracamy rzeczywistego obiektu, ale odniesienie do obiektu.

Od php.net : Odwołanie do PHP jest aliasem, który umożliwia zapisywanie dwóch różnych zmiennych do tej samej wartości. Od PHP 5 zmienna obiektowa nie zawiera już samego obiektu jako wartości. Zawiera tylko identyfikator obiektu, który umożliwia akcesoriom obiektowym znalezienie rzeczywistego obiektu. Gdy obiekt jest wysyłany przez argument, zwracany lub przypisywany do innej zmiennej, różne zmienne nie są aliasami: przechowują kopię identyfikatora, która wskazuje na ten sam obiekt.

Również w PHP5 + operator przypisania (=) jest inteligentny. Używa płytkiej kopii, a nie twardej kopii obiektu. Kiedy piszemy w ten sposób, $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];tylko dane zostaną skopiowane, a nie cały obiekt, ponieważ mają one ten sam typ obiektu.

Oto jeden przykład

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

Skutkuje:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Spróbuj zresetować zapytanie:

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

Skutkuje:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Możesz tworzyć problemy, nawet jeśli używasz 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'] ) ) );

Oczywiście rozwiązaniem byłoby wp_reset_queryponowne użycie funkcji.

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

Dlatego myślę, że query_postsmoże być lepiej z punktu widzenia pamięci. Ale zawsze powinieneś robić wp_reset_querysztuczki.

prosti
źródło
10

Właśnie utworzyłem nowy bilet trac, bilet nr 36874 , aby zaproponować wycofanie query_posts(). To, czy zostanie ono zaakceptowane, pozostaje dobrym pytaniem.

Prawdziwy duży problem query_posts()polega na tym, że wciąż jest powszechnie używany przez wtyczki i motywy, mimo że były naprawdę dobre napisy na temat tego, dlaczego NIGDY nie powinieneś go nigdy używać. Myślę, że najbardziej epickim postem na WPSE jest następujący:

deprecation! == usunięcie , więc wycofanie query_posts()nie zatrzyma jego użycia przez deweloperów niskiej jakości i ogólnie rzecz biorąc, którzy nie znają WordPress i używają samouczków niskiej jakości jako wytycznych. Podobnie jak jakiś dowód, jak wiele pytań wciąż mamy tu gdzie ludzie używają caller_get_postsw WP_Query? Od wielu lat jest przestarzały.

Przestarzałe funkcje i argumenty można jednak usunąć w dowolnym momencie, gdy rdzeń deweloperów uzna to za stosowne, ale najprawdopodobniej nigdy się tak nie stanie, query_posts()ponieważ spowoduje to uszkodzenie milionów witryn. Więc tak, prawdopodobnie nigdy nie zobaczymy całkowitego usunięcia query_posts()- co może prowadzić do tego, że najprawdopodobniej nigdy nie zostanie przestarzałe.

Jest to jednak punkt wyjścia, ale należy pamiętać, że wycofanie czegoś w WordPress nie przerywa jego użycia.

AKTUALIZACJA 19 maja 2016 r

Zgłoszony przeze mnie bilet jest teraz zamknięty i oznaczony jako duplikat 4-letniego biletu, który został zamknięty jako wontfix i został ponownie otwarty i nadal pozostaje otwarty i nierozwiązany.

Wygląda na to, że główni programiści trzymają się tego starego wiernego małego zła. Wszystkich zainteresowanych, oto duplikat starego 4-letniego biletu

Pieter Goosen
źródło
Dlaczego zamknęli bilet core.trac.wordpress.org/ticket/36874 ? Proszę @PieterGoosen, czy możesz dołączyć link do tego wątku w swoim bilecie core.trac.wordpress.org/ticket/36874, ponieważ to pytanie dotyczy biletu 1: 1
prosti
@prosti Wygląda na to, że został oznaczony jako duplikat, ponieważ ten problem został już poruszony ... 4 lata temu znaleziono tutaj .
Howdy_McGee
3

[nieco rant]

W tej chwili podstawową filozofią jest to, że nic nie jest naprawdę przestarzałe. Powiadomienie o wycofaniu, choć dobrze jest je mieć, zostanie zignorowane, jeśli funkcja nie zostanie w pewnym momencie odrzucona. Jest wiele osób, które nie rozwijają WP_DEBUGsię i nie zauważą powiadomienia, jeśli nie nastąpi rzeczywiste uszkodzenie.

OTOH, ta funkcja jest jak gotoinstrukcja. Osobiście nigdy (w przypadku mniejszej definicji niż się spodziewałem) użyłem, gotoale rozumiem argumenty wskazujące na sytuację, w której domyślnie nie jest to zło. To samo dotyczy query_posts, jest to prosty sposób na skonfigurowanie wszystkich globałów wymaganych do utworzenia prostej pętli i może być przydatny w kontekście ajax lub rest-api. Nigdy nie użyłbym tego również w tych kontekstach, ale widzę, że jest to raczej kwestia stylu kodowania niż funkcji samej w sobie złej.

Przechodząc nieco głębiej, głównym problemem jest to, że globalia muszą być w ogóle ustawione. To jest główny problem, a nie jedna funkcja, która pomaga je ustawić.

Mark Kaplun
źródło
Dla porównania jest naprawdę query_postswolniejszy niż zapytanie wtórne (czytaj: nie zapytanie główne).
prosti
@prosti, ponieważ po prostu ustawia i uruchamia wp_query, o ile wolniej może być? na pewno jest trochę narzutu, ale prawdopodobnie rozmawiamy tutaj o milisekundach. To oczywiście zakłada, że ​​używasz go w miejscach, w których WP domyślnie nie udostępnia zapytania. W miejscach, w których to robi, jest źle, nie query_postssam, ale bezużyteczne zapytanie, które zostało wykonane podczas ładowania WP
Mark Kaplun