Когда использовать WP_query (), query_posts () и pre_get_posts

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

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

Если я делаю дополнительные петли в любом месте страницы - на боковой панели, в нижнем колонтитуле, в любых «связанных сообщениях» и т. д. - Я хочу использовать WP_Query() . Я могу использовать это повторно на одной странице без какого-либо вреда. (правильно?).

То, что я точно не знаю

  1. Когда я использую @ nacin pre_get_posts против WP_Query() ? Должен ли я использовать pre_get_posts для всего сейчас?
  2. Когда я хочу изменить цикл на странице шаблона - допустим, я хочу изменить страницу архива таксономии - удаляю ли код if have_posts : while have_posts : the_post и напишите мой собственный WP_Query() ? Или я могу изменить вывод, используя pre_get_posts в моих функциях. php-файл?

TL; др

Правила tl; dr, которые я хотел бы извлечь из этого:

  1. Никогда не используйте query_posts
  2. При запуске нескольких запросов на одной странице используйте WP_Query()
  3. При изменении цикла сделайте это __________________.

Спасибо за любую мудрость

Терри

ps: Я видел и читал: Когда вы должны использовать WP_Query vs query_posts () vs get_posts ()? Что добавляет другое измерение - get_posts . Но не относится к pre_get_posts вообще.

148 голосов | спросил saltcod 1 Maypm12 2012, 17:04:08

5 ответов


137

Вы имеете право сказать:

  

Никогда не используйте 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() возвращает false ), хотя это может быть избыточным.). Основной запрос появляется в ваших шаблонах как:

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

Если вам когда-либо понадобится отредактировать этот цикл - используйте pre_get_posts. Если у вас возникает соблазн использовать query_posts() - вместо этого используйте pre_get_posts.

WP_Query

Основной запрос является важным экземпляром 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. Это возвращает массив объектов post. Методы, используемые в вышеприведенном цикле, больше не доступны для вас. Это не «Loop», а просто массив пост-объектов.

<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.
ответил Stephen Harris 1 Maypm12 2012, 17:27:31
53

Для циклов существуют два разных контекста:

  • основной , который происходит на основе запроса URL-адреса и обрабатывается до загрузки шаблонов
  • вторичные петли, которые происходят любым другим способом, вызванные из файлов шаблонов или иначе

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

Чтобы изменить основной цикл

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

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

Используйте new WP_Query или get_posts(), которые в значительной степени взаимозаменяемы (последний является тонкой оболочкой для прежних).

Очистка

Используйте wp_reset_query(), если вы использовали query_posts() или перепутались с глобальным $wp_query), поэтому вам почти не понадобится.

Используйте wp_reset_postdata(), если вы использовали the_post() или setup_postdata() или перепутались с глобальным $post и необходимо восстановить исходное состояние связанных с сообщением вещей.

ответил Rarst 1 Maypm12 2012, 17:27:52
22

Существуют допустимые сценарии использования query_posts($query), например:

  1. Вы хотите отобразить список сообщений или сообщений типа post-type на странице (используя шаблон страницы)

  2. Вы хотите сделать разбивку на страницы этих сообщений

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

  1. Это более интуитивно понятное для администратора (ваш клиент?) - они могут видеть страницу в «Странице»

  2. Лучше добавить его в меню (без страницы, им нужно будет добавить URL напрямую)

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

Вот упрощенный примерный код (который будет на вашем шаблоне страницы, например, на странице-странице-на-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();

// ...

Но зачем нам это делать, когда у нас есть такая приятная небольшая функция?

ответил Lukas Pecinka 16 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowSun, 16 Sep 2012 11:34:09 +0400 2012, 11:34:09
9

Я изменяю запрос WordPress из functions.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%')"; 
}
ответил T.Todua 23 Jpm1000000pmFri, 23 Jan 2015 13:11:29 +030015 2015, 13:11:29
6

Чтобы описать некоторые улучшения в принятом ответе, поскольку WordPress развивался с течением времени, и теперь некоторые вещи разные (через пять лет):

  

pre_get_posts - это фильтр для изменения любого запроса. Он чаще всего используется для изменения только «основного запроса»:

На самом деле это действие. Не фильтр, и это повлияет на любой запрос.

  

Основной запрос появляется в ваших шаблонах как:

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

Собственно, это также неверно. Функция have_posts выполняет итерацию объекта global $wp_query, который не связан only с основным запросом. global $wp_query; также может быть изменен с помощью вторичных запросов.

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

get_posts ()

     

Это по существу оболочка для отдельного экземпляра объекта WP_Query.

На самом деле, в настоящее время WP_Query - это класс, поэтому у нас есть экземпляр класса.


В заключение: в то время, когда @StephenHarris писал, что все это было правдой, но с течением времени вещи в WordPress были изменены.

ответил prosti 28 WedEurope/Moscow2016-12-28T04:26:41+03:00Europe/Moscow12bEurope/MoscowWed, 28 Dec 2016 04:26:41 +0300 2016, 04:26:41

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132