Когда использовать WP_Query, query_posts() или get_posts()?
Кажется, что половина руководств в Кодексе и блогах использует query_posts()
, а другая половина — WP_Query
.
Все они делают похожие вещи, так когда же стоит использовать один метод вместо других?

query_posts()
— это чрезмерно упрощенный и проблематичный способ изменить основной запрос страницы, заменяя его новым экземпляром запроса. Он неэффективен (повторно выполняет SQL-запросы) и может полностью отказать в некоторых случаях (особенно часто при работе с пагинацией записей). Любой современный код WordPress должен использовать более надежные методы, такие какpre_get_posts
, для этой цели. TL;DR: никогда не используйте query_posts().get_posts()
очень похож по использованию и принимает те же аргументы (с некоторыми нюансами, такими как разные значения по умолчанию), но возвращает массив записей, не изменяет глобальные переменные и безопасен для использования в любом месте.WP_Query
— это класс, который лежит в основе обоих методов, но вы также можете создать и работать с собственным экземпляром этого класса. Немного сложнее, меньше ограничений, также безопасен для использования в любом месте.
Источник изображения: https://www.rarst.net/images/query_functions.png

(1) "и безопасен для использования где угодно" --> но не используйте это для ОСНОВНОГО цикла. (2) не забудьте использовать global $query_string; перед строкой с query_posts();

@scribu однако 'get_posts' будет работать, хотя и не рекомендуется: http://core.trac.wordpress.org/ticket/16545

Я считаю, что query_posts
также менее эффективен, так как он выполняет дополнительные запросы, тогда как если вы используете только WP_Query
для основного цикла, будет выполнен только выбранный вами запрос в WP_Query.

@jjeaton query_posts()
— это небольшая функция-обёртка для WP_Query
, единственное дополнительное действие, которое она выполняет (согласно блок-схеме) — это перезапись глобальной переменной $wp_query

@Rarst Я имел в виду этот раздел в Codex для query_posts, однако, возможно, я ошибаюсь насчёт влияния на производительность. Если только использование WP_Query в вашем шаблоне не даст тот же результат (т.е. отбрасывание вашего запроса и его повторное выполнение)

@jjeaton Замена query_posts()
на WP_Query
не повлияет на производительность, исходный запрос страницы всё равно выполнится, так как это часть загрузки ядра. Эти запросы выполнятся, даже если в вашем файле шаблона вообще нет цикла.

Не могу избавиться от ощущения, что это самый гениальный и популярный пост на WPSE. Должен быть и в Codex тоже.

Ладно, после долгого изучения, думаю, что в query_posts()
не хватает статической переменной, которая устанавливается в true после первого использования и - если используется дважды - должна вызывать _doing_it_wrong();
. Пожалуй, я сообщу об этом ребятам из wp-hacker или trac.

@kaiser ну... использование query_posts()
дважды примерно так же плохо, как и один раз, для меня не имеет особого значения. :) кстати, Эндрю Нацин собирается делать презентацию о запросах и сказал, что может предложить некоторые улучшения к блок-схеме, так что вторая версия может появиться в будущем.

Добавлю самое понятное объяснение проблемы "производительности query_posts()": Использование query_posts() или WP_Query в файле шаблона будет иметь одинаковую стоимость производительности: запрос, который вы только что выполнили. Проблема, обсуждаемая в статье кодекса, заключается в том, что если вы действительно хотите заменить запрос, вам следует сделать это, отфильтровав исходный query_posts() с помощью фильтра 'parse_query'. Таким образом, у вас будет только один, исходный, желаемый запрос, вместо выполнения второго запроса для его неуклюжей замены. query_posts() — ЭТО НИКОГДА НЕ ПРАВИЛЬНОЕ РЕШЕНИЕ!! НИКОГДА!

Здесь не упоминается фильтр 'request', который является отличным способом изменить основной запрос. Преимущество перед query_posts заключается в том, что эта функция стирает исходный запрос и создает новый — так же, как если бы вы использовали WP_Query. Используя фильтр request, вы изменяете исходный запрос до того, как он будет отправлен. Думаю, это то, о чем говорил @JeremyClarke выше.

Есть потрясающее объяснение query_posts, написанное Джоном Джеймсом Джейкоби в блоге developer.wordpress.com, которое превосходит все эти ответы. Основная мысль: query_posts
не изменяет основной цикл, а заменяет его после того, как он уже выполнен. Лучший способ изменить основной цикл — использовать фильтр pre_get_posts
.
http://developer.wordpress.com/2012/05/14/querying-posts-without-query_posts/

@Dan ты путаешь техническую реализацию и предназначение. query_posts()
действительно заменяет объект основного цикла, но его цель - модифицировать основной цикл. Я также очень люблю фильтры циклов, но вопрос был не об этом. Есть уточняющий вопрос от другого человека на эту тему.

Вопрос звучал: "Когда следует использовать... query_posts()", и согласно логике, представленной в том блог-посте и комментариях выше, ответ, вероятно, - никогда.

@Manny Fleurmond концептуально query_posts()
— это попытка упростить концепции основного цикла до уровня тегов шаблонов тем (простота которых является одним из ключевых факторов популярности WP). Однако задача оказалась слишком сложной для тега шаблона. Разработчики ядра действительно высказывали возможность устаревания этой функции, но пока никакого решения по этому поводу не принято.

На самом деле нельзя "использовать где угодно" WP_Query(), я только что попробовал, и она все равно выдает ошибку на $thequery->have_posts(), бесконечная рекурсия, см. http://wordpress.stackexchange.com/questions/34270

@NoBugs цикл в том вопросе неправильный, и есть ответ, объясняющий почему.

Аааа, спасибо за это. Наконец-то что-то обрело смысл. Серьёзно, WordPress и их отвратительная документация. Не понимаю, как такое запутанное ПО с плохими стандартами кодирования стало настолько популярным.

Нашёл тест скорости между wp_query и get_posts http://www.wpclocked.com/

Я бы доверял такому тесту... ровно на ноль. :) Функция - это очень тонкая обёртка, любые различия будут вызваны небольшими различиями в аргументах и/или хуках.

Нет необходимости в эмоциях, query_posts() — это функция с побочными эффектами: она изменяет глобальную переменную. WordPress переполнен функциями с побочными эффектами. Это не проблема производительности, а вопрос качества кода. Посмотрите https://developer.wordpress.org/reference/functions/query_posts/ и узнайте, что делает query_posts. Используйте WP_Query, если не хотите испортить глобальные переменные.

query_posts
— никогда и ни при каких обстоятельствах не используйте query_posts
. Помимо того, что сказал @Rarst, главная проблема query_posts
заключается в том, что он ломает основной объект запроса (хранится в $wp_query
). Множество плагинов и пользовательского кода полагаются на основной объект запроса, поэтому его повреждение означает нарушение функциональности плагинов и пользовательского кода. Одна из таких функций — критически важная функция пагинации, так что, сломав основной запрос, вы сломаете пагинацию.
Чтобы убедиться, насколько плох query_posts
, выполните на любом шаблоне следующее и сравните результаты:
var_dump( $wp_query );
query_posts( '&posts_per_page=-1' );
var_dump( $wp_query );
get_posts
и WP_Query
— правильные способы создания вторичных запросов (таких как связанные записи, слайдеры, избранный контент и контент на статических главных страницах). Стоит отметить, что не следует использовать ни один из них вместо основного запроса на главной странице, странице записи или любой архивной странице, так как это нарушит работу пагинации. Если нужно изменить основной запрос, используйте pre_get_posts
, а не пользовательский запрос. (ОБНОВЛЕНИЕ: для статических главных страниц и обычных страниц см. Использование pre_get_posts на обычных страницах и статических главных страницах*)
По сути, WP_Query
используется основным запросом, а также get_posts
, но хотя get_posts()
использует WP_Query
, есть несколько различий:
get_posts
быстрее, чемWP_Query
. Разница зависит от общего количества записей на сайте. Причина в том, чтоget_posts
по умолчанию передаёт'no_found_rows' => true
вWP_Query
, что пропускает/законно ломает пагинацию. С'no_found_rows' => true
WP_Query
получает количество запрошенных записей и завершает работу, тогда как по умолчанию он продолжает искать все записи, соответствующие запросу, для расчёта пагинации.По этой причине
get_posts()
следует использовать только для запросов без пагинации. Пагинация сget_posts
— это настоящий бардак.WP_Query
следует использовать для всех запросов с пагинацией.get_posts()
не подвержены фильтрамposts_*
, тогда какWP_Query
подвержен. Причина в том, чтоget_posts
по умолчанию передаёт'suppress_filters' => true
вWP_Query
.get_posts
имеет несколько дополнительных параметров, таких какinclude
,exclude
,numberposts
иcategory
. Эти параметры преобразуются в валидные параметры дляWP_Query
перед передачей.include
преобразуется вpost__in
,exclude
— вpost__not_in
,category
— вcat
, аnumberposts
— вposts_per_page
. Важно отметить, что все параметры, которые можно передать вWP_Query
, работают сget_posts
, и вы можете игнорировать стандартные параметрыget_posts
.get_posts
возвращает только свойство$posts
объектаWP_Query
, тогда какWP_Query
возвращает полный объект. Этот объект очень полезен для условных операторов, пагинации и другой полезной информации, которую можно использовать внутри цикла.get_posts
не использует стандартный цикл, а циклforeach
для отображения записей. Также по умолчанию недоступны шаблонные теги. Для их использования необходимо вызватьsetup_postdata( $post )
.WP_Query
использует стандартный цикл, и шаблонные теги доступны по умолчанию.get_posts
передаёт'ignore_sticky_posts' => 1
вWP_Query
, поэтому по умолчанию игнорирует sticky-записи.
Исходя из вышесказанного, выбор между get_posts
и WP_Query
зависит от вас и ваших потребностей. Приведённая информация должна помочь вам сделать правильный выбор.

Хотел бы я иметь возможность добавлять ответы в избранное. Это так многое объясняет.

Отличное объяснение!
"get_posts() следует использовать только для запросов без пагинации. Пагинация в get_posts - это сплошная головная боль. WP_Query следует использовать для всех запросов с пагинацией" - по сути, это все, что нужно знать, на мой взгляд.

Основное различие заключается в том, что query_posts()
действительно предназначен только для модификации текущего цикла (Loop). После завершения необходимо сбросить цикл и продолжить работу. Этот метод также немного проще для понимания, просто потому что ваш "запрос" — это, по сути, строка URL, которую вы передаёте функции, например:
query_posts('meta_key=color&meta_value=blue');
С другой стороны, WP_Query
— это более универсальный инструмент, который больше похож на прямое написание MySQL-запросов, чем query_posts()
. Вы также можете использовать его где угодно (не только в цикле), и он не мешает другим выполняемым запросам.
Лично я чаще использую WP_Query
. Всё зависит от конкретной ситуации.

Просто нет необходимости использовать query_posts()
. Всё, что делает эта функция — создаёт новый объект WP_Query и перезаписывает им global wp_query
.
Для справки, вот фактический код функции query_posts()
:
function query_posts($query) {
$GLOBALS['wp_query'] = new WP_Query();
return $GLOBALS['wp_query']->query($query);
}
Создавайте свой собственный объект WP_Query, если вам нужно реализовать сложный пользовательский запрос. Или используйте get_posts()
, если вам требуется лишь небольшая модификация запросов.
В любом случае, я настоятельно рекомендую открыть wp_includes/query.php
и изучить класс WP_Query
.

- query_posts(): может использоваться только в одном случае — если вам нужно изменить основной запрос. Устанавливает множество глобальных переменных;
- get_posts(): работает очень похоже и принимает те же аргументы, но возвращает массив записей
- WP_Query: вы можете создавать и работать с собственным объектом этого класса. Немного сложнее, меньше ограничений, безопасно использовать где угодно.
