WP_Query в WordPress: продвинутая работа с запросами…

WP_Query в WordPress: продвинутая работа с запросами…

Класс WP_Query — это сердце выборки контента в WordPress. Без него не обходится ни один более-менее сложный сайт: кастомные списки записей, фильтры по категориям, выборка по произвольным полям, пагинация. Рассказываю, как выжать из WP_Query максимум и не просесть по производительности.

Что нужно знать о WP_Query? WP_Query — основной класс WordPress для выборки записей из БД. Позволяет фильтровать по десяткам параметров: тип записи, таксономии, мета-поля, даты, статусы. Главные правила: (1) всегда используйте pre_get_posts вместо прямого изменения основного запроса, (2) не вытаскивайте всё подряд — ограничивайте posts_per_page и поля через fields, (3) для сложных мета-запросов используйте meta_query с правильными типами сравнения.

Основы WP_Query: что внутри и зачем это знать

WP_Query — это не просто «ещё один способ получить записи». Это полноценный SQL-билдер с кэшированием, хуками и поддержкой пагинации. Когда вы вызываете new WP_Query( $args ), WordPress:

  1. Строит SQL-запрос на основе переданных параметров
  2. Выполняет запрос к базе данных
  3. Кэширует результаты (при правильной настройке)
  4. Наполняет глобальные переменные ($wp_query, $post)
  5. Вызывает хуки the_post, loop_start, loop_end

В отличие от get_posts(), WP_Query даёт полный контроль над пагинацией, подсчётом общего количества записей и навигацией.

Базовая структура

Код
$query = new WP_Query( array(
    'post_type'      => 'post',
    'posts_per_page' => 10,
    'orderby'        => 'date',
    'order'          => 'DESC',
) );

if ( $query->have_posts() ) :
    while ( $query->have_posts() ) : $query->the_post();
        the_title( '<h2>', '</h2>' );
        the_excerpt();
    endwhile;
    wp_reset_postdata();
endif;

Казалось бы, ничего сложного. Но дьявол — в деталях.

Мета-запросы: фильтрация по произвольным полям

Самый частый сценарий в реальных проектах — выборка записей по значениям произвольных полей (ACF, CMB2, или ручных meta-box).

Простая проверка meta_key

Код
$query = new WP_Query( array(
    'meta_key'   => 'price',
    'meta_value' => '1000',
    'meta_compare' => '>=',
    'type'       => 'NUMERIC',
) );

Множественные условия: meta_query

Код
$query = new WP_Query( array(
    'post_type'  => 'product',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'     => 'price',
            'value'   => array( 500, 5000 ),
            'compare' => 'BETWEEN',
            'type'    => 'NUMERIC',
        ),
        array(
            'key'     => 'in_stock',
            'value'   => 'yes',
            'compare' => '=',
        ),
    ),
) );

Параметр type критически важен. Без него WordPress сравнивает числа как строки, и «999» окажется больше «10000».

Поддержка ACF

Advanced Custom Fields умеет оптимизировать запросы через свои функции:

Код
$query = new WP_Query( array(
    'post_type'  => 'event',
    'meta_query' => array(
        array(
            'key'     => 'event_date',
            'value'   => date( 'Ymd' ),
            'compare' => '>=',
            'type'    => 'DATE',
        ),
    ),
    'orderby'    => 'meta_value',
    'meta_key'   => 'event_date',
    'order'      => 'ASC',
) );

Обратите внимание: orderby работает только в паре с meta_key. Если нужно сортировать по одному полю, а фильтровать по другому — обязательно указывайте meta_key для сортировки.

Таксономии: когда одной category не хватает

Кастомные таксономии — стандартный инструмент любого серьёзного проекта на WordPress.

Простая фильтрация по таксономии

Код
$query = new WP_Query( array(
    'post_type' => 'portfolio',
    'tax_query' => array(
        array(
            'taxonomy' => 'skill',
            'field'    => 'slug',
            'terms'    => 'web-design',
        ),
    ),
) );

Сложные условия с несколькими таксономиями

Код
$query = new WP_Query( array(
    'post_type' => 'portfolio',
    'tax_query' => array(
        'relation' => 'OR',
        array(
            'taxonomy' => 'skill',
            'field'    => 'slug',
            'terms'    => array( 'web-design', 'ux' ),
            'operator' => 'IN',
        ),
        array(
            'taxonomy' => 'technology',
            'field'    => 'slug',
            'terms'    => 'wordpress',
            'operator' => 'IN',
        ),
    ),
) );

Исключение категорий (NOT IN)

Код
$query = new WP_Query( array(
    'post_type'  => 'post',
    'tax_query'  => array(
        array(
            'taxonomy' => 'category',
            'field'    => 'slug',
            'terms'    => array( 'uncategorized' ),
            'operator' => 'NOT IN',
        ),
    ),
    'posts_per_page' => 12,
) );

Объединение meta_query и tax_query

Настоящая мощь WP_Query раскрывается, когда комбинируете оба типа запросов:

Код
$query = new WP_Query( array(
    'post_type'      => 'realty',
    'posts_per_page' => 20,
    'meta_query'     => array(
        array(
            'key'     => 'price',
            'value'   => 10000000,
            'compare' => '<=',
            'type'    => 'NUMERIC',
        ),
        array(
            'key'     => 'area',
            'value'   => 50,
            'compare' => '>=',
            'type'    => 'NUMERIC',
        ),
    ),
    'tax_query' => array(
        array(
            'taxonomy' => 'city',
            'field'    => 'slug',
            'terms'    => 'moscow',
        ),
        array(
            'taxonomy' => 'type',
            'field'    => 'slug',
            'terms'    => 'apartment',
        ),
    ),
    'orderby'  => 'meta_value_num',
    'meta_key' => 'price',
    'order'    => 'ASC',
) );

Этот запрос найдёт квартиры в Москве площадью от 50 м² с ценой до 10 млн рублей и отсортирует по возрастанию цены. Без WP_Query пришлось бы писать кастомный SQL с JOIN.

Дата, автор и статус — недооценённые параметры

Выборка по датам

Код
// Записи за последние 30 дней
$query = new WP_Query( array(
    'date_query' => array(
        array(
            'after'     => '30 days ago',
            'before'    => 'today',
            'inclusive' => true,
        ),
    ),
) );

// Записи за конкретный месяц
$query = new WP_Query( array(
    'date_query' => array(
        array(
            'year'  => 2025,
            'month' => 6,
        ),
    ),
) );

Фильтр по автору

Код
// Несколько авторов
$query = new WP_Query( array(
    'author__in' => array( 2, 5, 12 ),
) );

// Исключить автора
$query = new WP_Query( array(
    'author__not_in' => array( 1 ),
) );

Статусы записей

Код
$query = new WP_Query( array(
    'post_type'   => array( 'post', 'page' ),
    'post_status' => array( 'publish', 'draft', 'pending' ),
    'perm'        => 'readable',
) );

Производительность: частые ошибки и их решения

Самое больное место WP_Query — неоптимальные запросы, которые кладут базу на сайтах с десятками тысяч записей.

Ошибка 1: Выборка без ограничения полей

Код
// ПЛОХО: тянет все поля
$query = new WP_Query( array( 'posts_per_page' => 100 ) );

// ХОРОШО: только ID
$query = new WP_Query( array(
    'posts_per_page' => 100,
    'fields'         => 'ids',
) );

Параметр fields => 'ids' сокращает запрос с SELECT wp_posts.* до SELECT wp_posts.ID.

Ошибка 2: Игнорирование no_found_rows

Код
$query = new WP_Query( array(
    'posts_per_page' => 3,
    'no_found_rows'  => true,
) );

Без этого WordPress выполняет второй SQL-запрос SELECT COUNT(*). Если пагинация не нужна — отключайте.

Ошибка 3: Лишнее кэширование мета-полей и терминов

Код
$query = new WP_Query( array(
    'posts_per_page'          => 50,
    'fields'                  => 'ids',
    'update_post_meta_cache'  => false,
    'update_term_cache'       => false,
) );

Ошибка 4: WP_Query внутри цикла WP_Query

Если без вложенного запроса не обойтись — используйте wp_reset_postdata() и ставьте no_found_rows => true на внутреннем запросе. Лучше — вынесите данные во внешний запрос через meta_query и tax_query.

pre_get_posts: правильная модификация основного запроса

Вместо создания новых WP_Query копий часто правильнее модифицировать существующий запрос через хук pre_get_posts.

Изменение количества записей на странице архива

Код
add_action( 'pre_get_posts', function( $query ) {
    if ( ! is_admin() && $query->is_main_query() && is_post_type_archive( 'portfolio' ) ) {
        $query->set( 'posts_per_page', 12 );
        $query->set( 'orderby', 'menu_order' );
        $query->set( 'order', 'ASC' );
    }
} );

Исключение категории из главной

Код
add_action( 'pre_get_posts', function( $query ) {
    if ( ! is_admin() && $query->is_home() && $query->is_main_query() ) {
        $query->set( 'cat', '-7' );
    }
} );

Фильтрация кастомного архива по мета-полю

Код
add_action( 'pre_get_posts', function( $query ) {
    if ( ! is_admin() && $query->is_main_query() && $query->is_post_type_archive( 'event' ) ) {
        $meta_query = $query->get( 'meta_query' ) ?: array();
        $meta_query[] = array(
            'key'     => 'event_date',
            'value'   => date( 'Ymd' ),
            'compare' => '>=',
            'type'    => 'DATE',
        );
        $query->set( 'meta_query', $meta_query );
        $query->set( 'orderby', 'meta_value' );
        $query->set( 'meta_key', 'event_date' );
        $query->set( 'order', 'ASC' );
    }
} );

Без проверки is_main_query() вы сломаете административную панель и виджеты.

Заключение

WP_Query — мощный, но требовательный инструмент. Основные правила:

  • Используйте pre_get_posts для модификации основных запросов
  • Оптимизируйте запросы: fields => 'ids', no_found_rows, отключайте кэширование
  • Не делайте WP_Query внутри WP_Query — это убивает производительность
  • Комбинируйте meta_query и tax_query вместо кастомных SQL
  • Не забывайте про wp_reset_postdata() после каждого кастомного цикла

Хотите углубиться? Почитайте про свои поля оформления заказа в WooCommerce. А если только начинаете — загляните в раздел разработки WordPress.

728x90 9
Как получить только ID записей в WP_Query?

Используйте параметр 'fields' => 'ids'. Это сокращает SQL-запрос и снижает нагрузку на память.

Чем отличается WP_Query от get_posts?

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

Как отфильтровать записи по диапазону цен?

Используйте 'compare' => 'BETWEEN' с массивом: 'value' => array(1000, 5000). Обязательно указывайте 'type' => 'NUMERIC'.

Когда использовать pre_get_posts?

Если нужно изменить стандартный запрос на странице архива — используйте pre_get_posts. Новый WP_Query создавайте только для дополнительных выборок.

Как ускорить WP_Query на сайте с 50 000+ записей?

Три правила: (1) 'no_found_rows' => true если не нужна пагинация; (2) отключайте кэширование мета-полей и терминов; (3) для числовой сортировки используйте 'orderby' => 'meta_value_num'.

Можно ли сортировать записи по произвольному полю?

Да: укажите 'orderby' => 'meta_value' или 'meta_value_num' и 'meta_key' => 'field_name'.

Мощный SEO плагин Clearfy
Поделиться:
Telegram ВКонтакте