Когда использовать 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();
edelwater
@scribu однако 'get_posts' будет работать, хотя и не рекомендуется: http://core.trac.wordpress.org/ticket/16545
edelwater
Я считаю, что query_posts также менее эффективен, так как он выполняет дополнительные запросы, тогда как если вы используете только WP_Query для основного цикла, будет выполнен только выбранный вами запрос в WP_Query.
jjeaton
@jjeaton query_posts() — это небольшая функция-обёртка для WP_Query, единственное дополнительное действие, которое она выполняет (согласно блок-схеме) — это перезапись глобальной переменной $wp_query
Rarst
@Rarst Я имел в виду этот раздел в Codex для query_posts, однако, возможно, я ошибаюсь насчёт влияния на производительность. Если только использование WP_Query в вашем шаблоне не даст тот же результат (т.е. отбрасывание вашего запроса и его повторное выполнение)
jjeaton
@jjeaton Замена query_posts() на WP_Query не повлияет на производительность, исходный запрос страницы всё равно выполнится, так как это часть загрузки ядра. Эти запросы выполнятся, даже если в вашем файле шаблона вообще нет цикла.
Rarst
Не могу избавиться от ощущения, что это самый гениальный и популярный пост на WPSE. Должен быть и в Codex тоже.
kaiser
Ладно, после долгого изучения, думаю, что в query_posts() не хватает статической переменной, которая устанавливается в true после первого использования и - если используется дважды - должна вызывать _doing_it_wrong();. Пожалуй, я сообщу об этом ребятам из wp-hacker или trac.
kaiser
@kaiser ну... использование query_posts() дважды примерно так же плохо, как и один раз, для меня не имеет особого значения. :) кстати, Эндрю Нацин собирается делать презентацию о запросах и сказал, что может предложить некоторые улучшения к блок-схеме, так что вторая версия может появиться в будущем.
Rarst
Добавлю самое понятное объяснение проблемы "производительности query_posts()": Использование query_posts() или WP_Query в файле шаблона будет иметь одинаковую стоимость производительности: запрос, который вы только что выполнили. Проблема, обсуждаемая в статье кодекса, заключается в том, что если вы действительно хотите заменить запрос, вам следует сделать это, отфильтровав исходный query_posts() с помощью фильтра 'parse_query'. Таким образом, у вас будет только один, исходный, желаемый запрос, вместо выполнения второго запроса для его неуклюжей замены. query_posts() — ЭТО НИКОГДА НЕ ПРАВИЛЬНОЕ РЕШЕНИЕ!! НИКОГДА!
jerclarke
Здесь не упоминается фильтр 'request', который является отличным способом изменить основной запрос. Преимущество перед query_posts заключается в том, что эта функция стирает исходный запрос и создает новый — так же, как если бы вы использовали WP_Query. Используя фильтр request, вы изменяете исходный запрос до того, как он будет отправлен. Думаю, это то, о чем говорил @JeremyClarke выше.
eddiemoya
Есть потрясающее объяснение query_posts, написанное Джоном Джеймсом Джейкоби в блоге developer.wordpress.com, которое превосходит все эти ответы. Основная мысль: query_posts не изменяет основной цикл, а заменяет его после того, как он уже выполнен. Лучший способ изменить основной цикл — использовать фильтр pre_get_posts.
http://developer.wordpress.com/2012/05/14/querying-posts-without-query_posts/
Dan Gayle
@Dan ты путаешь техническую реализацию и предназначение. query_posts() действительно заменяет объект основного цикла, но его цель - модифицировать основной цикл. Я также очень люблю фильтры циклов, но вопрос был не об этом. Есть уточняющий вопрос от другого человека на эту тему.
Rarst
Вопрос звучал: "Когда следует использовать... query_posts()", и согласно логике, представленной в том блог-посте и комментариях выше, ответ, вероятно, - никогда.
Dan Gayle
@Manny Fleurmond концептуально query_posts() — это попытка упростить концепции основного цикла до уровня тегов шаблонов тем (простота которых является одним из ключевых факторов популярности WP). Однако задача оказалась слишком сложной для тега шаблона. Разработчики ядра действительно высказывали возможность устаревания этой функции, но пока никакого решения по этому поводу не принято.
Rarst
На самом деле нельзя "использовать где угодно" WP_Query(), я только что попробовал, и она все равно выдает ошибку на $thequery->have_posts(), бесконечная рекурсия, см. http://wordpress.stackexchange.com/questions/34270
NoBugs
@NoBugs цикл в том вопросе неправильный, и есть ответ, объясняющий почему.
Rarst
Аааа, спасибо за это. Наконец-то что-то обрело смысл. Серьёзно, WordPress и их отвратительная документация. Не понимаю, как такое запутанное ПО с плохими стандартами кодирования стало настолько популярным.
racl101
Нашёл тест скорости между wp_query и get_posts http://www.wpclocked.com/
Anagio
Я бы доверял такому тесту... ровно на ноль. :) Функция - это очень тонкая обёртка, любые различия будут вызваны небольшими различиями в аргументах и/или хуках.
Rarst
Нет необходимости в эмоциях, query_posts() — это функция с побочными эффектами: она изменяет глобальную переменную. WordPress переполнен функциями с побочными эффектами. Это не проблема производительности, а вопрос качества кода. Посмотрите https://developer.wordpress.org/reference/functions/query_posts/ и узнайте, что делает query_posts. Используйте WP_Query, если не хотите испортить глобальные переменные.
th00ht
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' => trueWP_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 зависит от вас и ваших потребностей. Приведённая информация должна помочь вам сделать правильный выбор.
Хотел бы я иметь возможность добавлять ответы в избранное. Это так многое объясняет.
InanisAtheos
Отличное объяснение!
"get_posts() следует использовать только для запросов без пагинации. Пагинация в get_posts - это сплошная головная боль. WP_Query следует использовать для всех запросов с пагинацией" - по сути, это все, что нужно знать, на мой взгляд.
Bullyen
Основное различие заключается в том, что 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: вы можете создавать и работать с собственным объектом этого класса. Немного сложнее, меньше ограничений, безопасно использовать где угодно.