Коли використовувати WP_query (), query_posts () та pre_get_posts


159

Я читав @ nacin " Ви не знаєте запиту вчора, і мені було надіслано трохи запитуючих кролячих отворів. До вчорашнього дня я (помилково) використовував query_posts()усі мої запити. Зараз я трохи розумніший у використанні WP_Query(), але все ще маю сірі ділянки.

Що я думаю, я точно знаю:

Якщо я роблю додаткові петлі де-небудь на сторінці - на бічній панелі, в нижньому колонтитулі, будь-які "пов’язані публікації" тощо, я хочу використовувати WP_Query(). Я можу це використовувати неодноразово на одній сторінці без будь-якої шкоди. (правда?).

Чого я точно не знаю

  1. Коли я використовую @ nacin в pre_get_posts VS. WP_Query()? Чи варто зараз використовувати pre_get_postsвсе?
  2. Коли я хочу змінити цикл на сторінці шаблону - скажемо, я хочу змінити сторінку архіву систематики таксономії - чи я видаляю if have_posts : while have_posts : the_postчастину і пишу власну WP_Query()? Або я можу змінити вихід, використовуючи pre_get_postsу своєму файлі function.php?

тл; д-р

Правила dr, які я хочу зробити з цього, є:

  1. Ніколи query_postsбільше не використовуйте
  2. Запускаючи кілька запитів на одній сторінці, використовуйте WP_Query()
  3. Змінюючи цикл, зробіть це __________________.

Дякую за будь-яку мудрість

Террі

ps: Я бачив і читав: Коли слід використовувати WP_Query vs query_posts () vs get_posts ()? Що додає ще один вимір - get_posts. Але це взагалі не займається pre_get_posts.



@saltcod, тепер відрізняється, WordPress еволюціонував, я додав кілька зауважень в порівнянні з прийнятим відповіддю тут .
prosti

Відповіді:


145

Ви праві сказати:

Ніколи query_postsбільше не використовуйте

pre_get_posts

pre_get_postsє фільтром, для зміни будь-якого запиту. Найчастіше використовується для зміни лише "основного запиту":

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

      if( $query->is_main_query() ){
        //Do something to main query
      }
}

(Я також би перевірив, що is_admin()повертається помилково - хоча це може бути зайвим.) Основний запит з’являється у ваших шаблонах у вигляді:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

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

WP_Query

Основний запит - важливий екземпляр a WP_Query object. WordPress використовує його, щоб визначити, який шаблон використовувати, наприклад, і будь-які аргументи, передані в URL-адресу (наприклад, розбиття на сторінки), усі каналізуються в цей примірник WP_Queryоб'єкта.

Для вторинних циклів (наприклад, у бічних смугах або списках "пов’язаних публікацій") вам потрібно створити свій окремий екземпляр WP_Queryоб'єкта. Напр

$my_secondary_loop = new WP_Query(...);
if( $my_secondary_loop->have_posts() ):
    while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post();
       //The secondary loop
    endwhile;
endif;
wp_reset_postdata();

Зауважте wp_reset_postdata();- це тому, що вторинний цикл замінить глобальну $postзмінну, яка ідентифікує "поточний пост". Це по суті скидає те, що $postми знаходимося.

get_posts ()

По суті це обгортка для окремого примірника WP_Queryоб'єкта. Це повертає масив об’єктів публікації. Методи, які використовуються у циклі вище, вам більше не доступні. Це не "цикл", а просто масив поштового об'єкта.

<ul>
<?php
global $post;
$args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 );
$myposts = get_posts( $args );
foreach( $myposts as $post ) :  setup_postdata($post); ?>
    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endforeach; wp_reset_postdata(); ?>
</ul>

У відповідь на ваші запитання

  1. Використовуйте pre_get_postsдля зміни вашого основного запиту. Використовуйте окремий WP_Queryоб’єкт (метод 2) для вторинних циклів на сторінках шаблонів.
  2. Якщо ви хочете змінити запит основного циклу, використовуйте pre_get_posts.

Так чи є сценарій, коли можна було б перейти прямо до get_posts (), а не до WP_Query?
urok93

@drtanz - так. Скажімо, наприклад, що вам не потрібна пагінація чи клейкі публікації вгорі - у цих випадках get_posts()ефективніше.
Стівен Гарріс

Але чи не це додасть додатковий запит, де ми могли б просто змінити pre_get_posts, щоб змінити основний запит?
urok93

@drtanz - ви б не використовували get_posts()для основного запиту - його для вторинних запитів.
Стівен Харріс

1
@StephenHarris Right =) Якщо ви використовуєте next_post () на об'єкті замість того, щоб використовувати the_post, ви не наступаєте на глобальний запит і не потрібно пам'ятати, щоб потім використовувати wp_reset_postdata.
Приватник

55

Існує два різних контексти для циклів:

  • основний цикл, який відбувається на основі запиту URL-адреси і обробляється перед завантаженням шаблонів
  • вторинні петлі, які відбуваються будь-яким іншим способом, викликаються з файлів шаблонів або іншим чином

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

Для зміни основного циклу

  • не використовувати query_posts()
  • використовувати pre_get_postsфільтр з $query->is_main_query()чеком
  • по черзі використовуйте requestфільтр (краще занадто грубо, тому вище краще)

Для запуску вторинного циклу

Використовуйте new WP_Queryабо get_posts()які в значній мірі взаємозамінні (останній - тонка обгортка для колишніх).

Для очищення

Використовуйте, wp_reset_query()якщо ви використовували query_posts()або заблукали з глобальним $wp_queryбезпосередньо - так що вам майже ніколи не знадобиться.

Використовуйте, wp_reset_postdata()якщо ви використовували the_post()або setup_postdata()заблукали з глобальним $postі вам потрібно відновити початковий стан речей, пов’язаних із публікацією.


3
Рарст мав на увазіwp_reset_postdata()
Григорій

23

Існують законні сценарії використання query_posts($query), наприклад:

  1. Ви хочете відобразити на сторінці список публікацій або користувацьких публікацій (використовуючи шаблон сторінки)

  2. Ви хочете, щоб ця сторінка працювала на сторінці

Тепер чому б ви хотіли відображати його на сторінці замість використання шаблону архіву?

  1. Для адміністратора (вашого клієнта?) Це більш інтуїтивно зрозуміло - вони можуть бачити сторінку у "Сторінках"

  2. Краще додавати його до меню (без сторінки, вони повинні будуть додавати URL безпосередньо)

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

Ось спрощений приклад коду (який буде у вашому шаблоні сторінки - наприклад, page-page-of-posts.php):

/**
 * Template Name: Page of Posts
 */

while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

// now we display list of our custom-post-type posts

// first obtain pagination parametres
$paged = 1;
if(get_query_var('paged')) {
  $paged = get_query_var('paged');
} elseif(get_query_var('page')) {
  $paged = get_query_var('page');
}

// query posts and replace the main query (page) with this one (so the pagination works)
query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged));

// pagination
next_posts_link();
previous_posts_link();

// loop
while(have_posts()) {
  the_post();
  the_title(); // your custom-post-type post's title
  the_content(); // // your custom-post-type post's content
}

wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data

// So, now we can display the page-related content again (if we wish so)
while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

Тепер, щоб бути абсолютно зрозумілим, ми могли б уникнути використання query_posts()тут і використовувати WP_Queryзамість цього:

// ...

global $wp_query;
$wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query

// your custom-post-type loop here

wp_reset_query();

// ...

Але чому б ми це робили, коли для нас є така приємна маленька функція?


1
Брайан, спасибі за це. Я намагаюся отримати так, щоб pre_get_posts працював на сторінці в ТОЧНО сценарії, який ви описуєте: клієнту потрібно додати користувацькі поля / вміст до того, що інакше було б сторінкою архіву, тому "сторінку" потрібно створити; клієнту потрібно побачити щось, що потрібно додати до меню nav, оскільки додавання спеціального посилання уникає їх; і т.д. +1 від мене!
Буде Ланні

2
Це також можна зробити за допомогою "pre_get_posts". Я зробив це для створення "статичної титульної сторінки", в якій перелічені мої типи публікацій у спеціальному порядку та зі спеціальним фільтром. Ця сторінка також страхіткована. Ознайомтесь з цим питанням, щоб побачити, як воно працює: wordpress.stackexchange.com/questions/30851/… Отже, коротше кажучи, ще немає законного сценарію використання query_posts;)
2ndkauboy

1
Оскільки "Слід зазначити, що використання цього для заміни основного запиту на сторінці може збільшити час завантаження сторінки, в гірших випадках більше ніж удвічі збільшити кількість роботи або більше. У той час як проста у використанні, функція також схильна до плутанини і проблеми згодом ". Джерело codex.wordpress.org/Function_Reference/query_posts
Claudiu

Ця відповідь - це всілякі помилки. Ви можете створити "Сторінку" в WP з тією ж URL-адресою, що і тип користувацької публікації. Якщо ваш CPT - це банани, ви можете отримати сторінку під назвою банани з тією ж URL-адресою. Тоді ви закінчите з siteurl.com/bananas. Поки ви маєте archive-bananas.php у папці з темами, він використовуватиме шаблон і замість нього замінить сторінку. Як зазначено в одному з інших коментарів, використання цього "методу" створює вдвічі більшу завантаженість для WP, тому НІКОЛИ НЕ слід використовувати.
Hybrid Web Dev

8

Я змінюю запит WordPress з function.php:

//unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour)
//so you can use `add_filter('posts_where', ....);`    OR   modify  "PAGE" query directly into template file

add_action( 'pre_get_posts', 'myFunction' );
function myFunction($query) {
    if ( ! is_admin() && $query->is_main_query() )  {
        if (  $query->is_category ) {
            $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) );
            add_filter( 'posts_where' , 'MyFilterFunction_1' ) && $GLOBALS['call_ok']=1; 
        }
    }
}
function MyFilterFunction_1($where) {
   return (empty($GLOBALS['call_ok']) || !($GLOBALS['call_ok']=false)  ? $where :  $where . " AND ({$GLOBALS['wpdb']->posts}.post_name NOT LIKE 'Journal%')"; 
}

Було б цікаво переглянути цей приклад, але там, де стаття в користувацьких мета.
Ендрю Велч

6

Просто окреслити деякі вдосконалення прийнятої відповіді, оскільки WordPress еволюціонував з часом, і деякі речі зараз відрізняються (через п’ять років):

pre_get_postsє фільтром, для зміни будь-якого запиту. Найчастіше використовується для зміни лише "основного запиту":

Насправді це гак дій. Не фільтр, і це вплине на будь-який запит.

Основний запит з’являється у ваших шаблонах у вигляді:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

Власне, це теж не вірно. Функція have_postsповторює global $wp_queryоб'єкт, не пов'язаний лише з основним запитом. global $wp_query;може бути змінено і з вторинними запитами.

function have_posts() {
    global $wp_query;
    return $wp_query->have_posts();
}

get_posts ()

По суті це обгортка для окремого екземпляра WP_Query об’єкта.

Насправді нині WP_Queryце клас, тому у нас є екземпляр класу.


На закінчення: У той час @StephenHarris писав, швидше за все, це було правдою, але з часом все в WordPress змінилося.


Технічно це все фільтри під капотом, дії - це просто простий фільтр. Але ви тут правильні, це дія, яка передає аргумент за посиланням, чим вона відрізняється від більш простих дій.
Міло

get_postsповертає масив поштових об'єктів, а не WP_Queryоб’єкт, так що це все-таки правильно. і WP_Queryзавжди був класом, екземпляром класу = об'єкта.
Міло

Дякую, @Milo, правильно, чомусь у мене була надто спрощена модель в голові.
prosti
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.