Если вам нужно вывести записи WordPress с нестандартной сортировкой, фильтрацией по таксономиям, мета-полям или датам — стандартный цикл have_posts() быстро перестаёт справляться. Класс WP_Query решает эту задачу: он позволяет построить практически любую выборку из базы данных WordPress без написания SQL-запросов.
Базовый запрос WP_Query
Простейший вызов WP_Query принимает массив аргументов и возвращает объект с результатами. После цикла обязательно вызывайте wp_reset_postdata(), чтобы восстановить глобальный объект $post.
<?php
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 10,
'post_status' => 'publish',
]);
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
echo '<h2>' . get_the_title() . '</h2>';
echo '<p>' . get_the_excerpt() . '</p>';
}
wp_reset_postdata();
}
Параметр post_type принимает строку или массив: 'post', 'page', 'any', или имя вашего CPT. Параметр posts_per_page со значением -1 вернёт все записи без лимита — используйте осторожно на больших сайтах.
Фильтрация по таксономиям (tax_query)
Параметр tax_query позволяет фильтровать записи по любой таксономии — категориям, меткам или кастомным таксономиям. Он принимает массив условий, которые можно комбинировать через оператор relation.
<?php
$query = new WP_Query([
'post_type' => 'product',
'posts_per_page' => 12,
'tax_query' => [
'relation' => 'AND',
[
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => ['electronics', 'gadgets'],
'operator' => 'IN',
],
[
'taxonomy' => 'product_tag',
'field' => 'term_id',
'terms' => [15, 22],
'operator' => 'NOT IN',
],
],
]);
Доступные операторы для tax_query:
- IN (по умолчанию) — запись принадлежит хотя бы одному из указанных терминов
- NOT IN — запись не принадлежит ни одному из терминов
- AND — запись принадлежит всем указанным терминам одновременно
- EXISTS / NOT EXISTS — у записи есть (или нет) хотя бы один термин данной таксономии
Параметр field определяет, как искать термины: по term_id, slug или name. Для производительности лучше использовать term_id — это избавляет WordPress от дополнительного запроса для конвертации слага в ID.
Фильтрация по мета-полям (meta_query)
С помощью meta_query можно фильтровать записи по произвольным полям (custom fields). Это один из самых мощных инструментов WP_Query, но и самый ресурсоёмкий — каждый элемент meta_query добавляет JOIN к SQL-запросу.
<?php
$query = new WP_Query([
'post_type' => 'product',
'posts_per_page' => 20,
'meta_query' => [
'relation' => 'AND',
'price_clause' => [
'key' => '_price',
'value' => [100, 5000],
'type' => 'NUMERIC',
'compare' => 'BETWEEN',
],
[
'key' => '_stock_status',
'value' => 'instock',
'compare' => '=',
],
],
'orderby' => [
'price_clause' => 'ASC',
],
]);
Обратите внимание на именованный ключ 'price_clause' — он позволяет использовать это мета-поле для сортировки через orderby. Доступные операторы сравнения: =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, EXISTS, NOT EXISTS.
Параметр type определяет приведение типа в SQL: NUMERIC, DECIMAL, CHAR, DATE, DATETIME, TIME. Без него WordPress сравнивает значения как строки, и '9' > '10' вернёт true.
Фильтрация по датам (date_query)
Параметр date_query фильтрует записи по дате публикации или изменения. Он поддерживает вложенные условия с relation.
<?php
// Записи за последние 30 дней, опубликованные в рабочее время
$query = new WP_Query([
'post_type' => 'post',
'date_query' => [
'relation' => 'AND',
[
'after' => '30 days ago',
'inclusive' => true,
],
[
'hour' => 9,
'compare' => '>=',
],
[
'hour' => 18,
'compare' => '<=',
],
],
]);
// Записи за конкретный месяц
$query = new WP_Query([
'post_type' => 'post',
'date_query' => [
[
'year' => 2026,
'month' => 3,
],
],
]);
Параметры after и before принимают строку, которую понимает strtotime(), или массив с ключами year, month, day. Параметр column позволяет фильтровать по post_date (по умолчанию), post_date_gmt, post_modified или post_modified_gmt.
Сортировка (orderby)
Параметр orderby принимает строку или массив для сортировки по нескольким полям. Основные значения:
- date — по дате публикации (по умолчанию)
- modified — по дате изменения
- title — по заголовку (алфавитный порядок)
- menu_order — по полю «Порядок» (для страниц и CPT)
- meta_value — по значению мета-поля (требует
meta_key) - meta_value_num — числовая сортировка по мета-полю
- rand — случайный порядок (медленно на больших таблицах)
- comment_count — по количеству комментариев
- post__in — в порядке массива из параметра
post__in
Множественная сортировка задаётся массивом:
<?php
$query = new WP_Query([
'post_type' => 'event',
'posts_per_page' => 20,
'meta_key' => 'event_date',
'orderby' => [
'meta_value' => 'ASC',
'title' => 'ASC',
],
]);
// Сортировка в порядке переданных ID
$featured_ids = [42, 15, 78, 3];
$query = new WP_Query([
'post_type' => 'post',
'post__in' => $featured_ids,
'orderby' => 'post__in',
'posts_per_page' => count( $featured_ids ),
]);
Пагинация
Для пагинации используется параметр paged. На архивных страницах WordPress автоматически передаёт номер страницы через query var paged, на статической главной — через page.
<?php
// Определяем текущую страницу
$paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 12,
'paged' => $paged,
]);
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
get_template_part( 'template-parts/content', get_post_type() );
}
// Навигация по страницам
echo paginate_links([
'total' => $query->max_num_pages,
'current' => $paged,
'format' => '?paged=%#%',
]);
wp_reset_postdata();
} else {
echo '<p>Записей не найдено.</p>';
}
Свойство $query->max_num_pages возвращает общее количество страниц, $query->found_posts — общее количество найденных записей. Эти свойства полезны для построения собственной навигации или вывода счётчика «Показано 1–12 из 48».
Оптимизация производительности
Несколько параметров помогут ускорить запросы, когда вам не нужны все данные:
'no_found_rows' => true— отключает подсчёт общего числа записей (SQL_CALC_FOUND_ROWS). Используйте, когда пагинация не нужна — это ускоряет запрос на 30-50%'update_post_meta_cache' => false— не загружать мета-данные записей в кеш. Полезно, если вы не используетеget_post_meta()в цикле'update_post_term_cache' => false— не загружать таксономии в кеш'fields' => 'ids'— вернуть только массив ID записей вместо полных объектов'cache_results' => false— не кешировать результаты (для одноразовых запросов в cron-задачах)
Помните: каждый элемент meta_query добавляет JOIN в SQL. Если вам нужна фильтрация по 3-4 мета-полям одновременно — рассмотрите создание сводной таблицы или перенос данных в таксономии. Три JOIN-а по wp_postmeta на таблице с 50 000 записей уже ощутимо тормозят.
Для отладки запросов включите SAVEQUERIES в wp-config.php и используйте плагин Query Monitor — он покажет все SQL-запросы, их время выполнения и место вызова в коде.
