15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать
21.10.2024

Что такое Tax Query в WordPress? Полное руководство разработчика

Запрос tax query в WordPress — это структурированный фильтр, передаваемый в WP_Query, который извлекает записи, соответствующие определённым таксономическим терминам. Вместо того чтобы загружать все записи из базы данных, tax query сужает результирующий набор только до тех записей, чьи связи с терминами удовлетворяют заданным условиям — будь то один слаг категории, комбинация терминов пользовательской таксономии или сложный паттерн исключения по нескольким таксономиям.

На практике: если вам нужно отобразить только записи с тегом «web-development», которые также относятся к пользовательской таксономии «project-type» с термином «client-work», tax query — это правильный и производительный инструмент для этой задачи. Он работает на уровне SQL через класс WP_Tax_Query WordPress, генерируя оптимизированные выражения JOIN и WHERE для таблиц wp_term_relationships и wp_term_taxonomy.

Структура таксономий и терминов в WordPress

Прежде чем писать хотя бы одну строку кода запроса, необходимо чётко понимать лежащую в основе архитектуру данных.

Таксономии — это системы классификации. WordPress поставляется с двумя глобальными таксономиями — category и post_tag — но функция register_taxonomy() позволяет определять неограниченное количество пользовательских. Таксономия — это по сути именованный механизм группировки.

Термины — это отдельные метки внутри таксономии. В таксономии category термины — это «Технологии», «Образ жизни» и «Бизнес». Каждый термин имеет три адресуемых идентификатора:

  • term_id — целочисленный первичный ключ в wp_terms
  • slug — URL-безопасный строковый идентификатор (например, web-development)
    name — удобочитаемая отображаемая метка
    
    Связи терминов — это сводные записи в wp_term_relationships, связывающие ID объекта записи с ID таксономии термина. Любой tax query в конечном счёте сводится к поиску по этой таблице.
    Понимание этой трёхуровневой структуры — таксономия > термин > связь термина — необходимо для написания эффективных запросов и диагностики неожиданных результирующих наборов.
    Основные аргументы параметра tax_query
    Ключ tax_query принимает массив из одного или нескольких массивов условий запроса, а также необязательный ключ relation верхнего уровня. Каждое условие поддерживает следующие аргументы:
    
    
    
    Аргумент
    Тип
    Описание
    Распространённые значения
    
    
    
    
    
    
    
    
    —
    —
    —
    —
    
    
    
    
    
    
    
    
    `taxonomy`
    string
    Таксономия для запроса
    `category`, `post_tag`, пользовательский слаг
    
    
    
    
    
    
    
    
    `field`
    string
    Поле термина для сопоставления
    `slug`, `name`, `term_id`, `term_taxonomy_id`
    
    
    
    
    
    
    
    
    `terms`
    string / int / array
    Значение(я) термина для сопоставления
    `'technology'`, `[4, 7]`, `'web-dev'`
    
    
    
    
    
    
    
    
    `operator`
    string
    Способ применения сопоставления термина
    `IN`, `NOT IN`, `AND`, `EXISTS`, `NOT EXISTS`
    
    
    
    
    
    
    
    
    `include_children`
    bool
    Включать ли дочерние термины (только для иерархических таксономий)
    `true` (по умолчанию), `false`
    
    
    
    
    
    
    
    
    `relation`
    string
    Логический соединитель верхнего уровня между несколькими условиями
    `AND`, `OR`
    
    
    
    
    
    Аргумент operator подробно
    Именно здесь большинство разработчиков допускают ошибки. Операторы работают следующим образом:
    
    IN (по умолчанию) — возвращает записи, назначенные *любому* из указанных терминов. Это сопоставление OR в рамках одного условия.
    NOT IN — исключает записи, назначенные любому из указанных терминов.
    AND — возвращает записи, назначенные *всем* указанным терминам одновременно. Используйте это, когда запись должна иметь несколько тегов одновременно.
    EXISTS — возвращает записи, имеющие *любой* термин в указанной таксономии, независимо от того, какой именно. Аргумент terms игнорируется.
    NOT EXISTS — возвращает записи без назначенного термина в указанной таксономии. Полезно для поиска контента без категорий или тегов.
    
    Важный нюанс: operator => 'AND' в рамках одного условия проверяет, что одна запись назначена каждому термину в массиве terms. Это отличается от использования relation => 'AND' на верхнем уровне, который объединяет отдельные условия. Путаница между этими двумя подходами — одна из наиболее распространённых ошибок tax query в рабочем коде WordPress.
    Базовый tax query: фильтр по одной таксономии
    Простейший случай использования — получить все записи в категории «Технологии»:
    $args = array(
        'post_type'  => 'post',
        'tax_query'  => array(
            array(
                'taxonomy' => 'category',
                'field'    => 'slug',
                'terms'    => 'technology',
            ),
        ),
    );
    
    $query = new WP_Query( $args );
    
    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            // Render post content here
        }
        wp_reset_postdata();
    }
    Всегда вызывайте wp_reset_postdata() после пользовательского цикла WP_Query. Невыполнение этого требования повреждает глобальный объект $post, что нарушает работу тегов шаблона, таких как the_title() и get_the_ID(), для любых последующих запросов на той же странице.
    Объединение нескольких tax query с помощью relation
    Ключ relation на верхнем уровне массива tax_query управляет тем, как объединяются несколько условий:
    $args = array(
        'post_type'  => 'post',
        'tax_query'  => array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'category',
                'field'    => 'slug',
                'terms'    => 'technology',
            ),
            array(
                'taxonomy' => 'post_tag',
                'field'    => 'slug',
                'terms'    => 'web-development',
            ),
        ),
    );
    Это возвращает только записи, которые *одновременно* находятся в категории «Технологии» и имеют тег «web-development». Замените relation на 'OR', и вы получите записи, соответствующие любому из условий.
    Вложенные tax query (WordPress 4.1+)
    Для расширенной логики фильтрации WordPress поддерживает вложенные массивы tax_query, позволяя строить составные булевы выражения:
    $args = array(
        'post_type'  => 'product',
        'tax_query'  => array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'product_cat',
                'field'    => 'slug',
                'terms'    => array( 'laptops', 'desktops' ),
                'operator' => 'IN',
            ),
            array(
                'relation' => 'OR',
                array(
                    'taxonomy' => 'product_tag',
                    'field'    => 'slug',
                    'terms'    => 'sale',
                ),
                array(
                    'taxonomy' => 'availability',
                    'field'    => 'slug',
                    'terms'    => 'in-stock',
                ),
            ),
        ),
    );
    Это извлекает товары в категориях «Ноутбуки» или «Настольные компьютеры», которые *также* либо распродаются, либо есть в наличии. Такую вложенную логику невозможно корректно воспроизвести с помощью плоского ключа relation — вложенные массивы являются единственным правильным подходом.
    Пользовательские типы записей и пользовательские таксономии
    Tax query становятся особенно мощными в сочетании с пользовательскими типами записей, зарегистрированными через register_post_type(), и пользовательскими таксономиями через register_taxonomy(). Рассмотрим сайт-портфолио, где вы регистрируете тип записи portfolio и таксономию portfolio_type:
    // Register custom taxonomy (typically in functions.php or a plugin)
    register_taxonomy(
        'portfolio_type',
        'portfolio',
        array(
            'label'        => 'Portfolio Types',
            'hierarchical' => true,
        )
    );
    
    // Query portfolio items of type "branding"
    $args = array(
        'post_type'  => 'portfolio',
        'tax_query'  => array(
            array(
                'taxonomy' => 'portfolio_type',
                'field'    => 'slug',
                'terms'    => 'branding',
            ),
        ),
    );
    
    $query = new WP_Query( $args );
    Когда hierarchical имеет значение true и include_children явно не установлен в false, запрос автоматически включает все дочерние термины «branding». Это корректное поведение для иерархий в стиле категорий, но оно может давать неожиданные результаты при наличии глубоко вложенных деревьев терминов. Устанавливайте 'include_children' => false, когда требуется сопоставление только с точным термином.
    Использование term_id vs. slug vs. name в качестве значения field
    
    
    
    Значение поля
    Когда использовать
    Предостережение
    
    
    
    
    
    
    
    
    —
    —
    —
    
    
    
    
    
    
    
    
    `slug`
    Жёстко заданные запросы в коде темы/плагина
    Слаги могут быть изменены редакторами в административной панели
    
    
    
    
    
    
    
    
    `term_id`
    Критичные по производительности или программные запросы
    ID различаются между окружениями (разработка vs. продакшн)
    
    
    
    
    
    
    
    
    `name`
    Удобочитаемая логика уровня отображения
    Чувствительно к регистру; ненадёжно при редактировании названий
    
    
    
    
    
    
    
    
    `term_taxonomy_id`
    Устранение неоднозначности в нескольких таксономиях
    Редко требуется; используйте только при коллизии ID терминов между таксономиями
    
    
    
    
    
    Для рабочего кода slug в целом является наиболее безопасным выбором с точки зрения читаемости. Однако при миграции баз данных между тестовой и рабочей средой учитывайте, что значения term_id будут различаться. Всегда используйте slug для переносимого кода.
    Соображения о производительности и распространённые ошибки
    Нагрузка на базу данных
    Каждый tax query генерирует как минимум один дополнительный JOIN к wp_term_relationships. При наличии нескольких условий это накапливается. На сайтах с десятками тысяч записей плохо составленные tax query являются одной из основных причин медленной загрузки страниц и таймаутов базы данных.
    Ключевые оптимизации:
    
    Используйте fields => 'ids', когда вам нужны только ID записей, а не полные объекты записей. Это позволяет избежать загрузки метаданных записей и сериализованных данных.
    Кэшируйте результаты с помощью Transients API. Tax query на страницах архивов, которые редко изменяются, следует кэшировать с помощью set_transient() и get_transient().
    Избегайте operator => 'AND' с большими массивами терминов. Каждый дополнительный термин в условии AND добавляет подзапрос. Выполняйте бенчмаркинг с помощью EXPLAIN в MySQL перед развёртыванием.
    Устанавливайте no_found_rows => true, когда пагинация не требуется. Это позволяет пропустить накладные расходы SQL_CALC_FOUND_ROWS.
    
    $args = array(
        'post_type'      => 'post',
        'fields'         => 'ids',
        'no_found_rows'  => true,
        'tax_query'      => array(
            array(
                'taxonomy' => 'category',
                'field'    => 'slug',
                'terms'    => 'technology',
            ),
        ),
    );
    Ловушка wp_reset_postdata()
    Если вы запускаете вторичный WP_Query внутри основного цикла без сброса данных записи, глобальная переменная $post будет указывать на неправильную запись до конца рендеринга страницы. Это вызывает незаметные ошибки: неверные заголовки записей в хлебных крошках, некорректные канонические URL и сломанные теги Open Graph. Всегда выполняйте сброс.
    Запрос несуществующих терминов
    Если вы передаёте значение terms, которого нет в базе данных, WP_Query молча возвращает ноль результатов. Никакой ошибки или предупреждения не выдаётся. Всегда проверяйте существование термина с помощью term_exists() перед построением динамических tax query на основе пользовательского ввода или внешних данных.
    $term = term_exists( 'technology', 'category' );
    if ( $term !== 0 && $term !== null ) {
        // Safe to build the tax query
    }
    Реальные сценарии использования
    Пользовательские страницы архивов
    Переопределите archive.php или используйте pre_get_posts для внедрения tax query в основной запрос, фильтруя архив для отображения только определённых терминов без создания отдельного объекта запроса:
    add_action( 'pre_get_posts', function( $query ) {
        if ( ! is_admin() && $query->is_main_query() && is_post_type_archive( 'portfolio' ) ) {
            $query->set( 'tax_query', array(
                array(
                    'taxonomy' => 'portfolio_type',
                    'field'    => 'slug',
                    'terms'    => array( 'branding', 'web-design' ),
                    'operator' => 'IN',
                ),
            ) );
        }
    } );
    Использование pre_get_posts эффективнее, чем создание вторичного WP_Query, поскольку оно изменяет основной запрос до обращения к базе данных.
    Фильтрация товаров в интернет-магазине
    WooCommerce регистрирует product_cat и product_tag как стандартные таксономии WordPress, а также таксономии атрибутов, такие как pa_color и pa_size. Tax query обеспечивают работу боковой панели многоуровневой навигации с фильтрами. Пользовательский фильтр для «красных ноутбуков определённого бренда» объединял бы три отдельных условия таксономии с relation => 'AND'.
    Редакционное исключение контента
    Используйте operator => 'NOT IN' для исключения спонсорского или рекламного контента из редакционных лент:
    $args = array(
        'post_type'  => 'post',
        'tax_query'  => array(
            array(
                'taxonomy' => 'post_tag',
                'field'    => 'slug',
                'terms'    => array( 'sponsored', 'promoted' ),
                'operator' => 'NOT IN',
            ),
        ),
    );
    Поиск неклассифицированного контента
    Используйте operator => 'NOT EXISTS' для аудита библиотеки контента на предмет записей без обязательных назначений таксономии:
    $args = array(
        'post_type'  => 'post',
        'tax_query'  => array(
            array(
                'taxonomy' => 'category',
                'operator' => 'NOT EXISTS',
            ),
        ),
    );
    Это незаменимо для контент-аудитов на крупных редакционных сайтах, где записи могли быть импортированы без надлежащего назначения таксономии.
    Tax query vs. Meta query: выбор правильного инструмента
    Распространённое архитектурное решение при разработке на WordPress — хранить ли данные фильтрации как термин таксономии или как метаданные записи. Этот выбор имеет существенные последствия для производительности.
    
    
    
    Критерий
    Tax query (`tax_query`)
    Meta query (`meta_query`)
    
    
    
    
    
    
    
    
    —
    —
    —
    
    
    
    
    
    
    
    
    Таблица базы данных
    `wp_term_relationships` (индексированная)
    `wp_postmeta` (менее оптимизирована для фильтрации)
    
    
    
    
    
    
    
    
    Производительность запросов
    Высокая — разработана для поиска по наборам
    Медленнее при масштабировании — структура EAV
    
    
    
    
    
    
    
    
    Фасетная фильтрация
    Нативная, эффективная
    Требует обходных решений
    
    
    
    
    
    
    
    
    Тип данных
    Контролируемый словарь (термины)
    Произвольные пары ключ-значение
    
    
    
    
    
    
    
    
    Сценарий использования
    Категоризация, классификация
    Атрибуты, измерения, флаги
    
    
    
    
    
    
    
    
    Индексирование
    Автоматическое через ID таксономии терминов
    Требует ручной настройки индексов
    
    
    
    
    
    Практическое правило: если данные используются для фильтрации или навигации (цвет, категория, тип, статус), используйте таксономию. Если это уникальный атрибут каждой записи (цена, вес, временная метка публикации), используйте метаданные записи. Путаница между этими подходами — одна из наиболее распространённых причин медленной работы WordPress-сайтов при масштабировании.
    Соображения о хостинге для WordPress-сайтов, использующих сложные запросы
    Tax query с несколькими условиями, вложенной логикой или большими наборами терминов генерируют сложный SQL. Производительность этих запросов во многом зависит от серверного окружения.
    На плане VPS Хостинга у вас есть прямой контроль над конфигурацией MySQL — вы можете настраивать innodb_buffer_pool_size, включать кэш запросов (MySQL 5.7 и более ранние версии) и добавлять пользовательские индексы к wp_term_relationships при необходимости. Общие окружения, как правило, не позволяют такого уровня настройки базы данных.
    Если вы управляете высоконагруженным магазином WooCommerce с многоуровневой навигацией на основе tax query, Выделенный сервер обеспечивает изолированный ввод-вывод базы данных, что устраняет проблему «шумного соседа», снижающую время отклика запросов на общей инфраструктуре.
    Для разработчиков, которым нужно удобство панели управления при сохранении серверного доступа для оптимизации базы данных, VPS с cPanel предоставляет практичный компромисс — полный доступ к MySQL через phpMyAdmin наряду с привычным интерфейсом управления.
    Сайтам, активно использующим конечные точки WordPress REST API на основе tax query, также следует рассмотреть объектное кэширование (Redis или Memcached) на уровне сервера, которое настраивается на Панелях управления VPS, поддерживающих пользовательские слои кэширования PHP и на стороне сервера.
    Матрица принятия решений и технический чеклист
    Перед развёртыванием tax query в рабочей среде проверьте следующее:
    
    Существование термина проверено — используйте term_exists() для любых динамически формируемых значений терминов
    wp_reset_postdata() вызван — после каждого пользовательского цикла WP_Query, без исключений
    include_children явно задан — не полагайтесь на значение по умолчанию для иерархических таксономий, если включение дочерних элементов не является намеренным
    fields => 'ids' используется — везде, где полные объекты записей не требуются
    no_found_rows => true задан — для любого запроса, не требующего пагинации
    Результаты кэшированы — используйте Transients API для запросов на высоконагруженных страницах архивов или лендингах
    pre_get_posts предпочтителен — по сравнению с вторичными экземплярами WP_Query для модификаций основного запроса
    Выбор operator подтверждён — различайте IN (любой термин), AND (все термины) и NOT IN (исключение) перед написанием условия
    Различие relation и operator понято — relation соединяет условия; operator управляет сопоставлением внутри условия
    Вложенные массивы используются для составной логики — не пытайтесь выразить комбинации AND/OR только с помощью плоского ключа relation
  • Запрос к базе данных зафиксирован и проверен — используйте плагин Query Monitor или SAVEQUERIES для проверки сгенерированного SQL перед запуском

Часто задаваемые вопросы

В чём разница между relation => 'AND' и operator => 'AND' в tax query?

relation — это ключ верхнего уровня, соединяющий несколько условий tax query между собой — он определяет, должна ли запись удовлетворять всем условиям (AND) или хотя бы одному (OR). operator — это ключ уровня условия, определяющий, как массив terms сопоставляется в рамках одного условия — AND требует, чтобы записи были назначены все перечисленные термины одновременно.

Почему мой tax query не возвращает результатов, хотя записи и термины существуют?

Наиболее распространённые причины: передача значения terms, не соответствующего указанному типу field (например, передача имени, когда field установлен в slug), запрос к таксономии, не зарегистрированной для данного типа записей, или использование operator => 'AND' с терминами, которыми ни одна запись не обладает одновременно. Включите SAVEQUERIES и проверьте сырой SQL для диагностики.

Можно ли использовать tax query внутри WordPress REST API?

Да. WP_REST_Posts_Controller REST API принимает tax_query косвенно через зарегистрированные параметры запроса. Для пользовательских таксономий необходимо установить 'show_in_rest' => true при регистрации таксономии. Для сложных многоусловных запросов используйте пользовательскую конечную точку REST, которая формирует аргументы WP_Query на стороне сервера.

Влияет ли include_children => true на производительность?

Да. Когда include_children включён (по умолчанию для иерархических таксономий), WordPress выполняет дополнительный запрос для получения всех ID дочерних терминов перед построением основного запроса. На таксономиях с глубокими иерархиями и большим количеством терминов этот предварительный запрос добавляет ощутимые накладные расходы. Устанавливайте 'include_children' => false, когда требуется точное сопоставление терминов без наследования дочерних терминов.

Существует ли ограничение на количество условий в tax query?

В ядре WordPress нет жёстко заданного ограничения, однако практические ограничения накладываются максимальной глубиной соединений MySQL и порогами сложности запросов. Более четырёх-пяти условий в одном tax query — это сигнал о том, что модель данных может нуждаться в пересмотре: через денормализацию, выделенный поисковый индекс (Elasticsearch, Typesense) или реструктуризацию иерархии таксономии для сокращения количества условий.

15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать