Обзорquery_posts()get_posts()WP_Query()

В данной статье рассматриваются вариант построения циклов для вывода записей и о плюсах и минусах каждого из них.

Правильное использование нескольких циклов на странице даст вам возможность выводить блоки с нужными записями, сортировать их в нужном порядке и при этом не переживать о нарушении логической структуры страницы и «ловле» различных багов.

Возможные варианты построения циклов вывода записей:

  1. Стандартный цикл и цикл на основе query_posts().
  2. Дополнительный цикл на основе get_posts().
  3. Дополнительный цикл на основе WP_Query().

Каждый из этих вариантов удобно использовать в разных ситуациях:

  • query_posts() — если нужно изменить/подправить стандартный вывод записей на страницах WordPress. Можно использовать 1 раз на странице.
  • get_posts() — если нужно вывести записи из Базы Данных. Можно использовать сколько угодно раз на странице.
  • WP_Query() — во всех других случаях когда не подошли query_posts() и get_posts(). Класс WP_Query() является ядром query_posts() и get_posts() и может быть использован для каких-либо сложных случаев вывода.

Стандартный Цикл и цикл на основе query_posts()

Рассмотрим 2 вида циклов (с query_posts() и начинающийся с if( have_posts() ), потому что технически они абсолютно одинаковые.

Давайте вспомним, как выглядит стандартный Цикл WordPress:

<?php
// проверяем есть ли посты в глобальном запросе - переменная $wp_query
if( have_posts() ){
	// перебираем все имеющиеся посты и выводим их
	while( have_posts() ){
		the_post();
		?>

		<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">
			<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
			<?php the_content(); ?>
		</div>

		<?php
	}
	?>

	<div class="navigation">
		<div class="next-posts"><?php next_posts_link(); ?></div>
		<div class="prev-posts"><?php previous_posts_link(); ?></div>
	</div>

	<?php
}
// постов нет
else {
	echo "<h2>Записей нет.</h2>";
}

Такой код мы можем встретить в файлах index.phpcategory.php и т.д. Эти файлы отвечают за вывод на странице списка записей. Этот цикл перебирает по очереди посты, которые выводятся на странице и во время перебора, используя Теги Шаблона (предназначенные для использования внутри Цикла), мы можем вывести различные данные поста (заголовок, текст, метаданные и т.д.).

Обратите внимание: в стандартном Цикле мы не указываем никаких данных для выборки записей, а сразу начинаем цикл с if( have_posts() ){ while( have_posts() ){… Это говорит о том, что данные уже существуют и их нужно просто обработать и вывести на экран.

«Уже существующие» данные находятся в глобальной переменной $wp_query и для каждого типа страниц WordPress определяются автоматически, т.е. WordPress заранее делает запрос в БД, на основе того какая страница сейчас отображается (категория, метка, статья, постоянная страница и т.д.) и результат запроса записывается в $wp_query, а затем мы используем эти данные для создания цикла. Интересно, что такой запрос делается функцией query_posts(), которую мы разберем ниже.

Обычный Цикл WordPress используется для базовых страниц WP (категории, метки, архивы по дате).

Цикл на основе query_posts()

query_posts() позволяет изменить базовый запрос и вывести нужный нам вариант записей.

Вариант 1

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

<?php
global $query_string; // параметры базового запроса

// базовый запрос + свои параметры
query_posts( $query_string .'&cat=-6,-9&order=ASC&posts_per_page=20' );

[СТАНДАРТНЫЙ ЦИКЛ WORDPRESS]

wp_reset_query(); // сброс запроса
?>

В этом примере мы создали новый запрос к БД, в котором использовали параметры базового запроса + свои параметры: исключили категории 6 и 9 (cat=-6,-9), а также отсортировали записи по порядку (order=ASC) и вывели 20 записей на странице вместо, установленных в настройках 10 (posts_per_page=20).

Полный список параметров, которыми можно сформировать нужный нам вывод, смотрите в описании функции query_posts().

Преимущества такого изменения в том, что если мы, например, изменим количество выводимых записей на странице с 10 (по умолчанию) на 20, то пагинация на странице автоматически подстроится под это изменение, потому что query_post() меняет данные глобальной переменной $wp_query, а пагинация строится именно на основе этих данных. Это лишь один из примеров, показывающий что query_posts() и поведение других функций на странице взаимосвязаны.

Но все же, изменять базовый запрос WP рекомендуется через фильтр pre_get_posts, а не через query_posts().

Вариант 2

Можно не использовать параметры базового запроса ($query_string), а полностью переписать базовый запрос:

query_posts( 'cat=-6,-9&order=ASC' );

Однако, такой подход по сути сотрет базовый запрос и создаст новый, который может быть составлен неправильно, поэтому полностью переписывать базовый запрос не рекомендуется. Лучше постараться решить задачу как-то по-другому.

Необходимость wp_reset_query()

Сбрасывать измененный запрос при использовании query_posts() нужно, потому что query_posts() переписывает глобальную переменную $wp_query которая отвечает за некоторые свойства страницы. Давайте посмотрим на примере.

Предположим нам на странице категории 6 (ID категории), нужно вывести данные только поста 9 (ID поста):

<?php
query_posts( 'p=9' );

if( have_posts() ){
	while( have_posts() ){
		the_post();

		the_title();
		the_content();
	}
}
else {
	echo 'Записей нет';
}

?>

В этом примере мы не сбросили запрос и функция query_posts() переписала глобальную переменную $wp_query. Теперь, кода мы проверим какая это страница (а это страница категории: is_category() == true), мы увидим, что это уже совсем не страница категории, а страница поста: is_single() == true. Т.е. следующий код вернет нам «Это страница поста», хотя на самом деле это страница категории:

if( is_category() ) echo 'Это страница категории'; // не сработает
if( is_single() )   echo 'Это страница поста';     // сработает

Ошибочка, которая может в последствии создать немало головной боли.

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

Когда нужно немного изменить основной (базовый) запрос WordPress. В идеале:

  • для исключения рубрики/метки (например, на главной странице).
  • изменение направления сортировки.
  • ограничения количества выводимых постов
  • исключения определенных постов из категории/метки
  • и т.п.

И напомню еще раз, для таких задач все же лучше использовать фильтр pre_get_posts.

Не нужно использовать query_posts() для создания нескольких циклов на одной странице, для вывода в сайдбар списка постов, для создания дополнительного вывода записей и т.п. Для этих целей используйте циклы на основе get_posts(). Они используют одни и те же параметры.

Цикл на основе get_posts()

Самый удобный вариант выводить нужные записи в нужном порядке — это выводить их с помощью get_posts(). get_posts() чаще всего подходит лучше под вашу задачу, например:

  • нужно вывести 10 последних постов в сайдбаре.
  • нужно вывести 10 случайных записей в подвале.
  • нужно вывести все картинки прикрепленные к посту.
  • нужно вывести записи с определенным произвольным полем.

get_posts() так же как и query_posts() работает на основе класса WP_Query() и поэтому передаваемые параметры одинаковые.

Важной особенностью этой функции является то, что она форсированно устанавливает параметры:

  • suppress_filters=true (можно изменить)
  • posts_per_page=5 (можно изменить)
  • ignore_sticky_posts=true (нельзя изменить)
  • no_found_rows=true (нельзя изменить)

Последние два параметра значительно ускоряют запрос. Чем больше постов на сайте, тем больше будет разница в скорости работы функции get_posts() и WP_Query.

Пример цикла на основе get_posts()

<?php

$args = [
    'post_type' => 'post',
    // args...
];

$posts = get_posts($args);

foreach ($posts as $post) {
?>
    <div class="post">
        <h2><?php echo $post->post_title; ?></h2>
        <div class="content">
            <p><?php echo $post->post_content; ?></p>
        </div>
    </div>
<?php
}

Данный цикл получает посты с типом post, вместо него также можно указывать произвольные типы записей. В переменную args можно указать дополнительные аргументы такие, как количество записей, категория (таксономия), значение мета поля и т.д.

Когда использовать get_posts()?

  • Всегда, когда нужно просто вывести записи из БД в любом месте шаблона.
  • Когда нужно создать несколько циклов.
  • Когда не нужно знать сколько всего записей подходят под выборку.
  • Так как get_posts() принимает те же параметры что и query_posts(), её очень удобно использовать для вывода записей по самым разным критериям.

Цикл на основе WP_Query()

Для вывода постов никак не связанных с текущей страницей или для создания множественных (дополнительных) циклов можно использовать циклы на основе класса WP_Query. Выглядят они аналогично циклам с использование query_posts(). Для WP_Query используются те же самые параметры, что и для query_posts().

Интересно, что WP_Query является ядром функций query_posts() и get_posts(), т.е. обе эти функции работают на основе этого класса.

Пример цикла: выведем записи типа post

<?php

$args = [
    'post_type' => 'post',
    'posts_per_page' => 5,
    // args...
];

$query = new WP_Query($args);

foreach ($query->posts as $post) {
?>
    <div class="post">
        <h2><?php echo $post->post_title; ?></h2>
        <div class="content">
            <p><?php echo $post->post_content; ?></p>
        </div>
    </div>
<?php
}

Особенность циклов на WP_Query() в том, что мы создаем новый объект $query, который никак не связан с аналогичным глобальным объектом $wp_query и поэтому мы никак не нарушаем структуру текущей страницы.

Также, мы можем использовать новый объект в других целях, не только для вывода записей, но и для различного рода проверок: например, записи какой страницы используются в этом новом объекте; можем узнать общее количество записей удовлетворяющих запросу ($query->found_posts) и т.д. Такие данные могут пригодится при создании дополнительных запросов с пагинацией или где-то еще (например в комментариях).

Когда использовать WP_Query()?

  • Если нужно вывести записи не затрагивая основной цикл (допустим записи в боковой панели).
  • Если нужно создать множественные запросы.
  • Если нужно сколько всего постов удовлетворяют условиям выборки (для этого в SQL запрос добавляется флаг SQL_CALC_FOUND_ROWS — это создает дополнительную нагрузку на запрос). Так циклы WP_Query() дополнительно нагружают запрос, то чаще нам будет подходить циклы с помощью get_posts().