Чому query_posts () не позначається як застарілий?


15

query_posts()Технічно кажучи дві функції. Одне query_posts()насправді, WP_Query::query_posts()а інше - у глобальному просторі.

Прохання з розуму:

Якщо глобальне query_posts()- це «зло», чому його не заставляють?

Або чому не позначено як _doing_it_wong.


2
Це чудове запитання! Для інших, хто стикається з цим, хто не знає, чому не слід використовувати query_posts (), ось і ось кілька хороших грунтовних запитань на нього.
Тім Малоун

Відповіді:


11

Основне питання

Рити Давайте в тріо: ::query_posts, ::get_postsі class WP_Queryзрозуміти ::query_postsкраще.

Наріжним каменем для отримання даних у WordPress є WP_Queryклас. Обидва методи ::query_postsі ::get_postsвикористовують цей клас.

Зауважте, що клас WP_Queryмістить також методи з тим самим іменем: WP_Query::query_postsі WP_Query::get_posts, але ми насправді розглядаємо лише глобальні методи, тому не плутайтесь.

введіть тут опис зображення

Розуміння WP_Query

Клас покликаний WP_Queryбув представлений ще в 2004 році. Усі поля, що мають позначку ☂ (парасолька), там, де вони були ще в 2004 році. Додаткові поля були додані пізніше.

Ось 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 - швейцарський армійський ніж.

Деякі речі про WP_Query:

  • це те, що ви можете контролювати за допомогою аргументів, які ви передаєте
  • за замовчуванням воно жадібне
  • вона містить речовину для циклічного циклу
  • вона зберігається в глобальному просторі x2
  • він може бути первинним або вторинним
  • він використовує допоміжні класи
  • він має зручний pre_get_postsгачок
  • він навіть підтримує вкладені петлі
  • він містить рядок запиту SQL
  • вона містить кількість результатів
  • він тримає результати
  • він містить список усіх можливих аргументів запиту
  • він містить прапори шаблону
  • ...

Я не можу пояснити все це, але деякі з них складні, тому давайте короткі поради.

WP_Query це те, що ви можете контролювати за допомогою аргументів, які ви передаєте

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

Цей список з версії 4.7 WordPress, безумовно, зміниться в майбутньому.

Це був би мінімальний приклад створення WP_Queryоб'єкта з аргументів:

// 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 жадібний

Створений за ідеєю, get all you canрозробники WordPress вирішили отримати всі можливі дані рано, оскільки це добре для продуктивності . Ось чому за замовчуванням, коли запит приймає 10 баз даних із бази даних, він також отримує умови та метадані для цих повідомлень за допомогою окремих запитів. Умови та метадані будуть кешовані (попередньо встановлені).

Зверніть увагу, що кешування призначене лише для одного запиту.

Ви можете відключити кешування, якщо ви встановили update_post_meta_cacheта, update_post_term_cacheпід falseчас встановлення WP_Queryаргументів. Якщо кешування відключено, дані будуть запитуватися з бази даних лише на вимогу.

Для більшості блогів WordPress кешування працює добре, але є випадки, коли ви можете відключити кешування.

WP_Query використовує допоміжні класи

Якщо ви перевірили WP_Queryполя, у вас є ці три:

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

Ви можете уявити додавання нового в майбутньому.

введіть тут опис зображення

WP_Query утримує речовину для петління

У цьому коді:

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

ви можете помітити, що WP_Queryє речовина, яку ви можете повторити. Допоміжні методи також є. Ви просто встановите whileцикл.

Примітка. forа whileпетлі семантично рівнозначні.

WP_Query первинний і вторинний

У WordPress у вас є один первинний і нульовий або більше вторинних запитів.

Первинний запит можливо не мати, але це виходить за межі цієї статті.

Первинний запит, відомий як основний запит або звичайний запит . Вторинний запит також називається спеціальним запитом .

WordPress використовує WP_Rewriteклас рано для створення аргументів запитів на основі URL-адреси. На основі цих аргументів він зберігає два однакові об’єкти у глобальному просторі. Обидва вони матимуть основний запит.

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

Коли ми говоримо головний запит, ми думаємо про ці змінні. Інші запити можна назвати вторинними або власними.

Цілком легально використовувати або, global $wp_queryабо $GLOBALS['wp_query'], але використання другого позначення набагато помітніше, і це дозволяє економити введення додаткової лінії у межах функцій.

$GLOBALS['wp_query']і $GLOBALS['wp_the_query']є окремими об'єктами. $GLOBALS['wp_the_query']повинен залишатися замороженим.

WP_Queryмає зручний pre_get_postsгачок.

Це гак дій. Він застосовуватиметься до будь-якого WP_Query примірника. Ви називаєте це так:

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

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

 return $query;  
});

Цей гачок чудовий і він може змінювати будь-які аргументи запитів.

Ось що ви можете прочитати :

Запускається після створення об’єкта змінної запиту, але перед запуском фактичного запиту.

Таким чином, цей гачок є менеджером аргументів, але не може створювати нові WP_Queryоб'єкти. Якщо у вас був один основний і один вторинний запит, pre_get_postsне можна створити третій. Або якщо у вас був лише один основний, він не може створити вторинний.

Зверніть увагу, якщо вам потрібно змінити лише основний запит, ви також можете використовувати requestгачок.

WP_Query підтримує вкладені петлі

Цей сценарій може статися, якщо ви використовуєте плагіни, і ви викликаєте функції плагіна з шаблону.

Ось демонстраційний приклад WordPress має допоміжні функції навіть для вкладених циклів:

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;

Вихід буде таким, оскільки я встановив тестові дані тестового блоку :

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!

Незважаючи на те, що я попросив 5 повідомлень у користувацькому $-запиті, він поверне мені шість, тому що липкий пост буде продовжуватися. Якщо wp_reset_postdataв попередньому прикладі немає, вихід буде таким, через $GLOBALS['post']заповіт буде недійсним.

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 має wp_reset_query функцію

Це як кнопка скидання. $GLOBALS['wp_the_query']його слід постійно заморожувати, а плагіни або теми ніколи не повинні змінювати це.

Ось що wp_reset_queryробити:

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

Зауваження щодо get_posts

get_posts виглядає як

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

Номери рядків можуть змінитися в майбутньому.

Це просто обгортка навколо WP_Queryцього повертає повідомлення об’єкта запиту.

Якщо ignore_sticky_postsвстановити значення true, клейкі пости можуть відображатися лише в природному положенні. Спереду не буде липких постів. Інший параметр no_found_rowstrue означає, що API бази даних WordPress не буде використовуватись SQL_CALC_FOUND_ROWSдля реалізації сторінки розширення, зменшуючи навантаження на базу даних для виконання знайдених підрахунків рядків .

Це зручно, коли вам не потрібна сторінка. Тепер ми розуміємо, що можемо імітувати цю функцію за допомогою цього запиту:

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

Ось відповідний запит 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

Порівняйте те, що ми маємо зараз, з попереднім запитом SQL там, де він SQL_CALC_FOUND_ROWSіснує.

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

Запит без SQL_CALC_FOUND_ROWS буде швидшим.

Зауваження щодо query_posts

Порада: Спочатку у 2004 році був лише global $wp_query. Станом на версію WordPress 2.1 $wp_the_queryвийшла версія . Порада: $GLOBALS['wp_query']і $GLOBALS['wp_the_query']є окремими об’єктами.

query_posts()є WP_Queryобгорткою. Він повертає посилання на головний WP_Queryоб'єкт, і в той же час він встановлює global $wp_query.

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

У PHP4 все, включаючи об'єкти, передавались за значенням. query_postsбуло так:

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

Зверніть увагу, що в типовому сценарії з одним основним та одним вторинним запитом у нас є ці три змінні:

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

Скажімо, кожен з цих трьох займає 1М пам'яті. Всього буде 3М пам'яті. Якщо ми будемо використовувати query_posts, $GLOBALS['wp_query']буде знято і буде створено заново.

PHP5 + має бути розумним спорожненням $GLOBALS['wp_query']об'єкта, як і в PHP4, як ми це робили зunset($GLOBALS['wp_query']);

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

У результаті query_postsспоживається 2М пам'яті загалом, тоді як get_postsспоживається 3М пам'яті.

Зауважте, query_postsми повертаємо не власне об'єкт, а посилання на об'єкт.

З php.net : Посилання PHP - це псевдонім, який дозволяє двом різним змінним записати на одне значення. Як і в PHP 5, змінна об'єкта вже не містить сам об'єкт як значення. Він містить лише ідентифікатор об'єкта, який дозволяє об'єкторам об'єктів знаходити фактичний об'єкт. Коли об'єкт надсилається аргументом, повертається або призначається іншій змінній, різні змінні не є псевдонімами: вони містять копію ідентифікатора, яка вказує на той самий об'єкт.

Також у PHP5 + оператор назначення (=) розумний. Він буде використовувати дрібну копію, а не важку копію об'єкта. Коли ми пишемо так, $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];скопіюються лише дані, а не весь об'єкт, оскільки вони мають один і той же тип об'єкта.

Ось один приклад

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

Результат:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Спробуйте скинути запит:

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

Результат:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Ви можете створювати проблеми, навіть якщо використовуєте 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'] ) ) );

Звичайно, рішенням було б знову використовувати 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'] ) ) );

Тому я думаю, query_postsщо з точки зору пам'яті це може бути краще. Але завжди слід робити wp_reset_queryхитрість.


10

Я щойно створив новий квиток на проїзд, квиток № 36874 , щоб запропонувати анулюванняquery_posts() . Чи буде це прийнято чи ні, залишається гарним питанням.

Справжня велика проблема з query_posts()тим, що вона все ще широко використовується плагінами та темами, навіть незважаючи на те, що були справді хороші писання на тему, чому вам НІКОЛИ НІКОЛИ не користуватися цим. Я думаю, що найпопулярніший пост тут на WPSE - такий:

депрекація! == видалення , тому застарілість query_posts()не зупинить її використання неякісними розробниками та людьми взагалі, які не знають WordPress та які використовують підручники неякісної якості як настанови. Так само , як деякі докази, скільки питань ми до сих пір отримуємо тут , де люди використовують caller_get_postsвWP_Query ? Це вже давно застаріло.

Проте застарілі функції та аргументи можна видалити в будь-який час, коли основні розробники вважають за потрібне, але це, швидше за все, ніколи не станеться, query_posts()оскільки це зламає мільйони сайтів. Так, так, ми, мабуть, ніколи не побачимо повного видалення query_posts()- що може призвести до того, що воно, швидше за все, ніколи не буде застарілим.

Це є відправною точкою, але потрібно пам’ятати, що знецінення чогось у WordPress не припиняє його використання.

ОНОВЛЕННЯ 19 травня 2016 року

Білет, який я підняв, тепер закритий і позначений як дублікат 4-річного квитка, який був закритий як wontfix і був відкритий, і досі залишається відкритим та невирішеним.

Здається, основні розробники звисають до цього старого вірного маленького зла. Всім зацікавленим, ось дублікат 4-річного старого квитка


Чому вони закрили квиток core.trac.wordpress.org/ticket/36874 ? Будь ласка, @PieterGoosen чи можете ви включити посилання на цю тему до свого квитка core.trac.wordpress.org/ticket/36874, оскільки це питання стосується квитка 1: 1
прості

@prosti Схоже, це було позначено як дублікат, оскільки це питання вже порушено ... 4 роки тому тут знайдено .
Howdy_McGee

3

[дещо рента]

На сьогоднішній день філософія, що стоїть на базі основного принципу, нічого по-справжньому застаріла. Повідомлення про депресію, хоча це приємно мати, просто ігнорується, якщо функція насправді не буде відкинута в якийсь момент. Є багато людей, які не розвиваються, WP_DEBUGі не помітять повідомлення, якщо не буде фактичної поломки.

З іншого боку, ця функція подібна до gotoтвердження. Особисто я ніколи (за меншим визначенням тоді не очікував) використовував, gotoале можу зрозуміти аргументи, що вказують на певну ситуацію, в якій за змовчуванням це не є зло. Те саме стосуєтьсяquery_posts , це простий спосіб встановити всі глобалі, необхідні для створення простого циклу, і може бути корисним в контексті ajax або rest-api. Я б ніколи не використовував це в цих контекстах, але я можу бачити, що там, це більше питання стилю кодування, а не функція, яка сама по собі зла.

Заглиблюючись трохи глибше, головна проблема полягає в тому, що глобали потрібно взагалі встановлювати. Це головна проблема не тієї функції, яка допомагає їх налаштувати.


А для порівняння, насправді query_postsповільніше, ніж вторинний запит (читай: не основний запит).
prosti

@prosti, оскільки він просто встановлює і запускає wp_query, наскільки це може бути повільніше? впевнений, що тут є якісь накладні витрати, але ми, мабуть, тут говоримо мілісекунди. Звичайно, це передбачає, що ви використовуєте його в місцях, де WP не надає запит за замовчуванням. У місцях, де це робить, це погано, не query_postsсам, а марний запит, який робився під час завантаження WP
Mark Kaplun
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.