Пользовательская настройка архивов WordPress для категорий, меток и других таксономий

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

Категории и метки — это примеры таксономий, и WordPress на самом деле позволяет создавать столько индивидуальных таксономий, сколько вы захотите. Эти пользовательские таксономии работают как категории или метки, но отдельно от них.

В этой статье мы будем говорить о пользовательских таксономиях и том, как их создавать. Мы также рассмотрим, какие файлы шаблонов темы WordPress связаны с встроенными архивами и пользовательскими таксономиями, а также некоторые передовые методы настройки архивов таксономии.

Содержание

Терминология

Прежде чем продолжить, давайте разберемся с терминами. Таксономии WordPress — это тип контента, используемый в основном для организации любого другого типа контента.

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

Категории и теги представляют два типа таксономии: иерархический и не иерархический. Иерархические таксономии, такие как категории, могут иметь подчиненные связи между терминами таксономии. Например, в вашем блоге может быть категория «фильмы«, которая имеет несколько дочерних категорий, с названиями вроде «иностранные» и «отечественные«.

Пользовательские таксономии могут также быть иерархическими, как категории, или не иерархическими, как теги:

«Иерархия шаблонов WordPress«

Архив таксономии — это список записей таксономии, который автоматически генерируется WordPress. Например, это может быть список страниц, который вы видите, когда нажимаете на ссылку категории, в нем вы можете видеть все записи этой категории. Мы займемся изменением поведения этих страниц и узнаем, какие файлы шаблонов генерируют их.

Как работают архивы меток, категорий и таксономий

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

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

Для этого в первую очередь нам нужно знать, какие файлы нашей темы используются для вывода архива. Различные темы имеют различные файлы шаблонов, но все темы содержат шаблон index.php. Шаблон index.php используется для отображения всего содержимого сайта, за исключение тех случаев, когда в иерархии шаблонов существует шаблон высшего порядка.

Иерархия шаблонов WordPress — это система, которая определяет, какой файл шаблона используется для отображения определенного контента. Мы вкратце рассмотрим иерархию шаблонов категорий, тегов и пользовательских таксономий.

Большинство тем содержат шаблон archive.php, который используется для архивов меток (тегов) и категорий, а также архивов по дате и по автору. Вы можете добавить отдельный файл шаблона для обработки архивов категорий и меток. Эти шаблоны будут называться category.php или tag.php, соответственно.

Кроме того, вы можете создавать шаблоны для отдельных тегов или категорий, с помощью ID или slug категории или тега. Например, тег с идентификатором 7 будет использовать шаблон tag-7.php, если он существует, а не tag.php или archive.php. Тег со slugavocado” будет отображаться с помощью шаблона tag-avocado.php.

Тут следует учитывать один нюанс — slug шаблона переопределяет порядок по идентификатору шаблона. Так, если тег со slugavocado” имел идентификатор 7, то предпочтительно будет использоваться шаблон tag-avocado.php, если он существует, а не tag-7.php.

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

Итак, представьте себе, что у вас есть две таксономии, «фрукты» и «овощи«, и в таксономии «фрукты» существуют два термина, «яблоки» и «апельсины«.

В таксономии «овощи» в свою очередь также существуют два термина: «морковь» и «сельдерей«. Давайте добавим в тему нашего сайта три шаблона: taxonomy.php, taxonomy-fruits.php и taxonomy-vegetables-carrots.php.

Для терминов таксономии «фрукты» все архивы будут выводиться с помощью шаблона taxonomy-fruits.php, потому что отдельных шаблонов для конкретных терминов не предусмотрено.

В то же время, термин «морковь» в архиве таксономии «овощи» будет выводиться с помощью шаблона taxonomy-vegetables-carrots.php. Так как отдельного шаблона taxonomy-vegetables.php не существует, все остальные термины таксономии «овощи» будут выводиться через шаблон taxonomy.php.

Использование тегов условий

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

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

Если мне нужен шаблон, который радикально отличается от шаблона темы archive.php, я, как правило, использую добавление условий в archive.php.

WordPress предоставляет функции условий для определения того, что отображается в категории, архиве меток или пользовательской таксономии.

Чтобы определить, применяется ли в настоящее время архив категории для вывода контента, вы можете использовать функцию is_category() для категорий, is_tag() для тегов и is_tax() для пользовательских таксономий. is_tag() и is_category() могут проверять конкретные категории или метки по slug или ID.

Например:

<?php
    if ( is_tag() ) {
        echo "True for any tag!";
    }
    if ( is_tag( 'jedis' ) ) {
        echo "True for the tag whose slug is jedi";
    }
    if ( is_tag( array( 'jedi', 'sith' ) ) ) {
        echo "True for tags whose slug is jedi or sith";
    }
    if ( is_tag( 7 ) ) {
        echo "You can also use tag IDs. This is true for tag ID 7";
    }
?>

Для пользовательских таксономий, функция is_tax() может использоваться для проверки любой таксономии (за исключением категорий и тегов), конкретной таксономии или конкретного термина таксономии.
Например:

<?php
    if ( is_tax() ) {
        echo "True for any custom taxonomy.";
    }
    if ( is_tax( 'vegetable' ) ) {
        echo "True for any term in the vegetable taxonomy.";
    }
    if ( is_tax( 'vegetable', 'celery' ) ) {
        echo "True only for the term celery, in the vegetable taxonomy.";
    }
?>

Создание пользовательской таксономии

Добавление пользовательской таксономии можно осуществить одним из трех способов: составление кода вручную в соответствии с инструкциями Кодекса, что я не рекомендую делать; генерация кода, с использованием GenerateWP; или с помощью плагинов для создания пользовательских типов контента, таких как Pods или Types.

Плагины для пользовательских типов контента позволяют создавать пользовательские таксономии и пользовательские типы записей, используя механику WordPress без необходимости писать код самому.

Их использование — это один из самых простых способов добавить пользовательскую таксономию и применить принцип фреймворка для работы с пользовательскими типами контента.

Если вы выберете один из первых двух вариантов, а не плагин, то вам нужно будет добавить код либо в файл functions.php темы, либо в собственный плагин. Я настоятельно рекомендую создать собственный плагин, а не добавлять код в functions.php. Даже если вы никогда прежде не создавали плагин, я рекомендую вам сделать это.

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

Если вы захотите написать собственный код таксономии, следуя указаниям Кодекса, или создать его с помощью GenerateWP, просто вставьте его в текстовый файл и добавьте в самом верху одну строку, и вы получите собственный плагин. Загружается и устанавливается он, как и любой другой плагин.

Единственной строкой, которую вы должны добавить, чтобы создать пользовательский плагин, является: /* Plugin name: Custom Taxonomy */.

Ниже приводится код плагина, который регистрирует пользовательскую таксономию “vegetables”. Я создал ее с помощью GenerateWP, потому что это значительно проще и гораздо меньше вероятность ошибок, чем, если бы я делал это вручную:

<?php
    /* Plugin Name: Veggie Taxonomy */
    if ( ! function_exists( 'slug_veggies_tax' ) ) {

    // Регистрация пользовательской таксономии
    function slug_veggies_tax() {

    $labels = array(
    'name'                      	=> _x( 'Vegetables', 'Taxonomy General Name', 'text_domain' ),
    'singular_name'              	=> _x( 'Vegetable', 'Taxonomy Singular Name', 'text_domain' ),
    'menu_name'                  	=> __( 'Taxonomy', 'text_domain' ),
    'all_Veggies'                   => __( 'All Veggies', 'text_domain' ),
    'parent_Veggie'                 => __( 'Parent Veggie', 'text_domain' ),
    'parent_Veggie_colon'           => __( 'Parent Veggie:', 'text_domain' ),
    'new_Veggie_name'               => __( 'New Veggie name', 'text_domain' ),
    'add_new_Veggie'                => __( 'Add new Veggie', 'text_domain' ),
    'edit_Veggie'                   => __( 'Edit Veggie', 'text_domain' ),
    'update_Veggie'                 => __( 'Update Veggie', 'text_domain' ),
    'separate_Veggies_with_commas'  => __( 'Separate Veggies with commas', 'text_domain' ),
    'search_Veggies'                => __( 'Search Veggies', 'text_domain' ),
    'add_or_remove_Veggies'         => __( 'Add or remove Veggies', 'text_domain' ),
    'choose_from_most_used'         => __( 'Choose from the most used Veggies', 'text_domain' ),
    'not_found'                     => __( 'Not Found', 'text_domain' ),
    );
    $args = array(
    'labels'                     => $labels,
    'hierarchical'               => false,
    'public'                     => true,
    'show_ui'                    => true,
    'show_admin_column'          => true,
    'show_in_nav_menus'          => true,
    'show_tagcloud'              => false,
    );
    register_taxonomy( 'vegetable', array( 'post' ), $args );

    }

    // Подключение к действию 'init'
    add_action( 'init', 'slug_veggies_tax', 0 );

    }
?>

Между прочим, в GenerateWP я создал этот код менее чем за две минуты! Отличный сервис, поэтому писать код вручную не имеет смысла, этот сайт может автоматически сгенерировать код для вас.

Чтобы сделать процесс еще проще, можно использовать плагин Pluginception, который создаст пустой плагин, а затем вставить в него код из GenerateWP с помощью редактора плагинов WordPress.

Использование WP_QUERY в пользовательских таксономиях

После того, как вы добавили пользовательскую таксономию, вы можете запросить записи по терминам этой таксономии. Для этого мы можем использовать запросы таксономии через WP_QUERY.

Запросы таксономии могут быть и очень простыми, и довольно сложными. Примером простейшего запроса является запрос всех записей с определенным термином. Например, если у вас есть тип записей под названием “jedi” и связанная с ним пользовательская таксономия под названием “level”, вы можете получить список всех рыцарей-магистров джедаев следующим образом:

<?php
    $args = array(
        'post_type' => 'jedi',
        'level' => 'master'
    );
    $query = new WP_Query( $args );
?>

Если вы добавили вторую пользовательскую таксономию под названием “era”, вы можете выбрать всех джедаев Старой Республики:

<?php
    $args = array(
        'post_type' => 'jedi',
        'level' => 'master',
        'era' => 'old-republic',
    );
    $query = new WP_Query( $args );
?>

Мы также можем сделать более сложные сравнения с использованием полного tax_query. Аргументы tax_query позволяют осуществлять поиск по ID вместо slug (как мы делали раньше) и искать более чем по одному термину.

Они также позволяют объединить несколько запросов таксономии и установить взаимосвязь между ними. Кроме того, мы можем даже использовать операторы SQL, такие как NOT IN, чтобы исключить условия.

Возможности бесконечны. Для получения дополнительной информации по “Class Reference/WP_Query” ознакомьтесь с разделом “Параметры таксономии” страницы Кодекса.

С помощью приведенного ниже фрагмента кода осуществляется поиск записей типа “jedi”, чтобы определить рыцарей-джедаев и магистров, которые не относятся к эпохе Старой Республики:

<?php
    $args = array(
        'post_type' => 'jedi',
        'tax_query' => array(
        'relation' => 'AND',
            array(
                'taxonomy' => 'level',
                'field' => 'slug',
                'terms' => array( 'master', 'knight' )
            ),
            array(
                'taxonomy' => 'era',
                'field' => 'slug',
                'terms' => array( 'old-republic' ),
                'operator' => 'NOT IN'
                )
        )
    );
    $query = new WP_Query( $args );
?>

Пользовательская настройка архивов таксономии

До сих пор мы рассматривали, как работают таксономии, теги и категории по умолчанию, а также как создавать собственные таксономии. Если поведение по умолчанию не соответствует вашим задачам, вы всегда можете его изменить.

Сейчас мы рассмотрим возможности изменения встроенного функционала WordPress. Это будет полезно для тех из вас, кто использует WordPress не как блог-платформу, а как систему управления контентом, для которой часто требуются пользовательские таксономии.

Знакомство с pre_get_posts

Перед выводом любых записей через цикл WordPress, система автоматически извлекает пользователю записи в соответствии со страницей, на которой они находятся. Делается это с помощью класса WP_QUERY. Например, на главной странице блога выводятся последние записи. В архиве таксономии, пользователю извлекаются последние записи таксономии.

Чтобы изменить этот запрос, вы можете перед извлечением записей использовать фильтр pre_get_posts. Этот фильтр уточняет объект запроса после того, как он установлен, но перед тем, как он непосредственно использован для получения записей.

Это означает, что вы можете изменить запрос с использованием методов класса перед запуском основного цикла WordPress. Если это звучит не совсем понятно для вас, не волнуйтесь — в следующих нескольких разделах этой статьи я приведу практические примеры того, как это работает.

Добавление пользовательских типов записей в категории или архивы меток

Огромное преимущество изменения объекта WP_QUERY с помощью pre_get_posts заключается в том, что мы можем добавить записи пользовательского типа в архив категории.

По умолчанию, пользовательские типы записей не включены в этот запрос. Если мы задаем аргументы, которые передаются в WP_QUERY и хотим включить в него как стандартные записи, так и записи пользовательского типа “jedi”, наш аргумент должен выглядеть следующим образом:

<?php
    $args = array( 'post_type' =>
        array(
            'post',
            'jedi'
        )
    );
?>

В функцию обратного вызова для фильтра pre_get_posts мы должны передать аналогичный аргумент. Проблема заключается в том, что объект WP_QUERY уже существует, поэтому мы не можем передать в него аргумент так, как мы делаем, когда создается экземпляр класса. Вместо этого мы используем метод класса set(), что позволяет нам изменять любой из аргументов после того, как класс был создан.

В приведенном ниже фрагменте кода мы используем set(), чтобы изменить аргумент post_type со значения по умолчанию, которое использует записи, на массив типов записей, в том числе записи и наш пользовательский тип записей “jedi”.

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

<?php
    add_filter( 'pre_get_posts', 'slug_cpt_category_archives' );
    function slug_cpt_category_archives( $query ) {
    if ( $query->is_category() && $query->is_main_query()  )  {
        $query->set( 'post_type',
            array(
                'post',
                'jedi'
            )
        );
    }

    return $query;

    }
?>

Параметр этой функции $query является объект WP_QUERY до того, как он задействуется для заполнения основного цикла.

Так как страница может содержать несколько циклов, например тех, которые используются виджетами, мы применяем функцию условия is_main_query(), чтобы сделать так, чтобы это влияло только на основной цикл, и не влияло на любые вторичные циклы страницы.

Создание категории или иерархической таксономии

Иерархии архивов

По умолчанию архивы категорий и других иерархических таксономий работают, как и любой другой архив таксономии: они выводят все записи в этой категории или с этим термином таксономии. Чтобы вывести только родительские термины и исключить дочерние, вы должны снова использовать фильтр pre_get_posts.

Так же, как при создании собственного WP_QUERY для записей таксономии, WP_QUERY основного цикла использует для получения записей таксономии аргументы tax_query.

tax_query имеет аргумент include_children, для которого по умолчанию установлено значение 1 или true. Изменив его на 0 или false, мы можем исключить из архива записи с дочерними терминами:

<?php
    add_action( 'pre_get_posts', 'slug_cpt_category_archives' );
    function slug_cpt_category_archives( $query ) {
        if ( is_tax( 'TAXONOMY NAME') )  {
            $tax_query = $query->tax_query->queries;
            $tax_query['include_children'] = 0;
            $query->set( 'tax_query', $tax_query );
        }

    }
?>

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

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

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

Кроме того, имейте в виду, что вы всегда можете обернуть вносимые изменения в теги условий, такие как is_category() или is_tax(), но очень скоро это может привести к захламлению кода; поэтому более логично будет сделать копию файла archive.php и удалить весь ненужный код.

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

Чтобы проделать все это для любой таксономии, которая может выводиться, мы должны получить текущую таксономию и термин таксономии из массива query_vars глобального объекта $wp_query. slug таксономии содержится в ключе таксономии, а slug термина — в ключе tax.

Для использования get_term_children(), нам нужен ID термина. ID не содержится в query_vars, но чтобы получить его, мы можем передать slug в get_term_by().

Вот как мы получаем в переменные всю нужную нам информацию:

<?php
    global $wp_query;
    $taxonomy = $wp_query->query_vars['taxonomy'];
    $term = $wp_query->query_vars['tax'];
    $term_id = get_term_by( 'slug', $term, $taxonomy );
    $term_id = $term_id->term_id;
    $terms = get_term_children( $term_id, $taxonomy );
?>

Мы продолжаем обработку только в том случае, если $terms не является пустым массивом. Чтобы проверить, не является ли он пустым, мы сначала повторно заполняем термины с помощью get_terms(). Это необходимо, потому что get_term_children возвращает только массив идентификаторов.

А нам нужны идентификаторы и имена, которые содержатся в объекте, возвращаемом get_terms(). Мы можем пропустить этот объект через цикл и вывести имена в виде ссылок. Ссылки можно сгенерировать, передав идентификаторы терминов в get_term_link().

Вот полный код:

<?php
    global $wp_query;
    $taxonomy = $wp_query->query_vars['taxonomy'];
    $term = $wp_query->query_vars['tax'];
    $term_id = get_term_by( 'slug', $term, $taxonomy );
    $term_id = $term_id->term_id;
    $terms = get_term_children( $term_id, $taxonomy );
    if ( !empty( $terms ) ) {
    $terms = get_terms( $taxonomy, array( 'child_of' => $term_id ) );
    echo '<ul class="child-term-list">';
    foreach ( $terms as $term ) {
    echo '<li><a href="'.$term->term_id.'">'.$term->name.'</a></li>';
    }

    echo '</ul>';

?>

Создание собственной целевой страницы таксономии архива

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

В этом случае можно создать собственную целевую страницу терминов. Мы снова используем query_vars, чтобы определить, находится ли пользователь на первой странице архива таксономии; если да, то мы используем фильтр taxonomy_archive, чтобы включить отдельный шаблон:

<?php
    add_filter( 'taxonomy_archive ', 'slug_tax_page_one' );
    function slug_tax_page_one( $template ) {
        if ( is_tax( 'TAXONOMY_NAME' ) ) {
             global $wp_query;
             $page = $wp_query->query_vars['paged'];
            if ( $page = 0 ) {
                $template = get_stylesheet_directory(). '/taxonomy-page-one.php';
            }
        }

        return $template;

    }
?>

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

Затем, получаем текущую страницу, используя переменную query_var с именем paged, и если пользователь находится на первой странице архива, то ему возвращается адрес нового файла шаблона. Если нет, возвращается файл шаблона по умолчанию.

Что вы вложите в этот отдельный шаблон, зависит только от вас. Вы можете создать список терминов, используя код, приведенный выше. На самом деле вы можете вывести что угодно — например, более подробную информацию о терминах таксономии или ссылки на конкретные записи.

Заключение

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

Использование пользовательских таксономий для организации контента в соответствии с вашими потребностями поможет вам в развитии многих ваших проектов на WordPress.

Надеюсь, эта статья поспособствует тому, чтобы вы полностью задействовали в своей работе мощные возможности, которые WordPress предоставляет разработчикам.

Данная публикация представляет собой перевод статьи «Customizing WordPress Archives For Categories, Tags And Other Taxonomies» , подготовленной дружной командой проекта Интернет-технологии.ру