Проверка отображения страницы архива пользовательской таксономии

22 нояб. 2015 г., 08:41:21
Просмотры: 33.4K
Голосов: 6

У меня есть пользовательский тип записи 'advert' и связанная с ним пользовательская таксономия 'advert_category', созданные сторонним плагином. В плагине я использую условие if, которое должно (как я ожидаю) установить определенный макет темы для отдельных объявлений (example.com/advert/a-single-advert.html) и для страниц архива пользовательской таксономии (example.com/advert-category/services/), но второе условие is_tax( 'advert_category' ) не работает. Что здесь не так?

Мой код:

function my_advert_single_template( ) {
    global $post;
    global $wpgo_global_column_layout;

    if ( $post->post_type == 'advert' || is_tax( 'advert_category' ) ) {
        $wpgo_global_column_layout = "2-col-l";
    }
}
add_filter( 'single_template', 'my_advert_single_template' );

Вот как зарегистрированы пользовательский тип записи и пользовательская таксономия:

// регистрация типа записи и таксономии для возможности вставки данных по умолчанию
register_post_type( 'advert' ); 
register_taxonomy( 'advert_category', 'advert' );

$hid = wp_insert_post(array(
    'post_type' => 'page',
    'post_status' => 'publish',
    'post_title' => 'Объявления',
    'comment_status' => 'closed',
    'ping_status' => 'closed',
    'post_content' => "[adverts_list]"
));

$aid = wp_insert_post(array(
    'post_type' => 'page',
    'post_status' => 'publish',
    'post_title' => 'Добавить',
    'post_parent' => $hid,
    'comment_status' => 'closed',
    'ping_status' => 'closed',
    'post_content' => "[adverts_add]"
));

$mid = wp_insert_post(array(
    'post_type' => 'page',
    'post_status' => 'publish',
    'post_title' => 'Управление',
    'post_parent' => $hid,
    'comment_status' => 'closed',
    'ping_status' => 'closed',
    'post_content' => "[adverts_manage]"
));

wp_insert_term(
    'По умолчанию',
    'advert_category'
);
2
Комментарии

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

Pieter Goosen Pieter Goosen
22 нояб. 2015 г. 09:09:34

Пожалуйста, перенесите найденное решение в отдельный ответ. :) Это больше соответствует механике работы сайта.

Rarst Rarst
22 нояб. 2015 г. 14:40:13
Все ответы на вопрос 2
5
10

У вас здесь много проблем:

  • pre_get_posts — это неправильный хук для установки шаблонов. pre_get_posts используется для изменения параметров основного запроса непосредственно перед тем, как SQL-запрос будет построен для выполнения основного запроса.

  • Фильтр всегда должен что-то возвращать. Если этого не делать, это приведет к непредсказуемому поведению, а забывчивость может заставить вас часами искать ошибку.

  • Использование глобальных переменных для управления функциями темы или хранения данных — это плохая практика и небезопасный код. WordPress уже создал огромный беспорядок с глобальными переменными, особенно с их именованием. Просто посмотрите, как новички (не знающие WordPress) неосознанно используют переменные вроде $post и $posts в качестве локальных. Это нативные глобальные переменные WordPress, и их использование в качестве локальных ломает их значения.

    Из-за этого что-то на странице работает неправильно, ошибок нет, и вы застреваете в бесконечном поиске проблемы, которую сами же и сломали. Глобальные переменные — это чистое зло, их следует избегать. Представьте: если вы используете переменную $wpgo_global_column_layout для аргументов пользовательского запроса, вы сломаете значение шаблона, который нужно установить. Ваш шаблон не загрузится, потому что значение $wpgo_global_column_layout не распознается как допустимое имя шаблона. Вы в тупике и не понимаете, почему ваш шаблон не загружается, хотя код на 100% должен это делать.

  • is_tax() — неправильная проверка для определения, имеет ли запись определенный термин. is_tax() просто проверяет, находитесь ли вы на архиве таксономии. Вместо этого следует использовать has_term(), который как раз и проверяет, есть ли у записи определенный термин.

  • Если вам нужно установить шаблон для страницы таксономии, single_template — неправильный хук. Вместо него следует использовать хук taxonomy_template или более универсальный фильтр template_include.

  • В строке $post->post_type == 'advert' || is_tax( 'advert_category' ), скорее всего, используется неверный оператор. Вместо || следует использовать AND. Я не буду объяснять это здесь, так как уже делал это здесь. Обратите внимание, что при текущей настройке условие будет возвращать true и срабатывать всякий раз, когда вы просматриваете запись типа advert, независимо от того, выполняется ли второе условие (is_tax( 'advert_category' )).

  • Если вам нужно определить термин по его положению в иерархии (родитель/потомок), достаточно проверить свойство $parent объекта термина. Значение 0 означает, что термин является родительским, любое другое значение указывает на дочерний/внучатый и т.д. термин.

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

ДЛЯ ОДИНОЧНЫХ СТРАНИЦ:

add_filter( 'single_template', function ( $template )
{
    // Удаляем все фильтры из текущего хука
    remove_all_filters( current_filter(), PHP_INT_MAX );
    
    /**
     * Получаем текущий объект записи. Используем get_queried_object,
     * так как это безопаснее, чем $post
     *
     * @see https://wordpress.stackexchange.com/q/167706/31545
     */
    $current_post = get_queried_object();
    
    // Проверяем, принадлежит ли текущая запись типу advert, если нет — выходим
    if ( $current_post->post_type !== 'advert' )
        return $template;
    
    // Получаем термины записи
    $terms = get_the_terms( 
        $current_post, // Объект текущей записи
        'advert_category' // Название таксономии
    );
    
    // Если $terms пуст или возвращает объект WP_Error — выходим
    if ( !$terms || is_wp_error( $terms ) )
        return $template;
    
    /**
     * Берем первый термин и проверяем, является ли он родительским.
     * Загружаем шаблон в зависимости от значения parent
     *
     * ПРИМЕЧАНИЕ: это работает корректно, только если у записи один термин
     */
    if ( $terms[0]->parent == 0 ) {
        $part = 'single-parent.php'; // Устанавливаем шаблон для родительских терминов
    } else {
        $part = 'single-child.php'; // Устанавливаем шаблон для дочерних терминов
    }
    
    // Проверяем, существует ли шаблон, если нет — выходим
    $locate_template = locate_template( $part );
    if ( !$locate_template ) 
        return $template;
    
    // Если дошли до этой точки — устанавливаем наш кастомный шаблон
    return $template = $locate_template;
}, PHP_INT_MAX + 1 );

ДЛЯ СТРАНИЦ ТАКСОНОМИЙ:

add_filter( 'taxonomy_template', function ( $template )
{
    // Удаляем все фильтры из текущего хука
    remove_all_filters( current_filter(), PHP_INT_MAX );

    // Получаем текущий объект термина. Используем get_queried_object
    $current_term = get_queried_object();
    
    // Если текущий термин не принадлежит таксономии advert_category — выходим
    if ( $current_term->taxonomy !== 'advert_category' )
        return $template;
    
    // Проверяем, является ли термин родительским, и устанавливаем шаблон
    if ( $current_term->parent == 0 ) {
        $part = 'taxonomy-parent.php'; // Устанавливаем шаблон для родительских терминов
    } else {
        $part = 'taxonomy-child.php'; // Устанавливаем шаблон для дочерних терминов
    }
    
    // Проверяем, существует ли шаблон, если нет — выходим
    $locate_template = locate_template( $part );
    if ( !$locate_template ) 
        return $template;
    
    // Если дошли до этой точки — устанавливаем наш кастомный шаблон
    return $template = $locate_template;
}, PHP_INT_MAX + 1 );

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

22 нояб. 2015 г. 08:49:44
Комментарии

Не работает :(. Согласно FunctionReference/has term: "Проверяет, содержит ли текущая запись любые из указанных терминов", так что это будет работать только на одиночных записях?

Iurie Iurie
22 нояб. 2015 г. 08:55:45

Это работает на одиночных страницах, в чем проблема. Не работает — это слишком общее описание

Pieter Goosen Pieter Goosen
22 нояб. 2015 г. 08:57:08

Я имею в виду, что страница 'example.com/advert-category/services/' не меняет шаблон.

Iurie Iurie
22 нояб. 2015 г. 09:00:07

Проверьте мое обновление. Надеюсь, это то, что вам нужно.

Pieter Goosen Pieter Goosen
22 нояб. 2015 г. 18:44:43

Питер, как всегда, отличный ответ! Большое спасибо!

Iurie Iurie
22 нояб. 2015 г. 21:27:20
2

В вашей функции есть две проблемы:

  • Вы пропускаете аргумент в фильтре "single_template", поэтому фильтр не возвращает то, что должен.
  • Также вы вызываете has_term() без указания таксономии, в которой пытаетесь выполнить поиск.

Вот исправленный вариант:

function my_advert_single_template( $single_template ) {
    global $post;
    global $wpgo_global_column_layout;

    if ( $post->post_type == 'advert' || has_term( 'util-categorie', 'advert_category' ) ) {
        $wpgo_global_column_layout = "2-col-l";
    }

    return $single_template;
}
add_filter( 'single_template', 'my_advert_single_template' );
22 нояб. 2015 г. 10:27:25
Комментарии

Я исправил некоторые ошибки в своём старом коде (это был тестовый вариант, извините, видимо, я ещё не проснулся). И я протестировал вашу функцию, но она не работает для страниц пользовательских таксономий, только для одиночных записей. И я нашёл решение (см. обновление).

Iurie Iurie
22 нояб. 2015 г. 10:45:56

Не беспокойтесь! Рад, что у вас получилось.

Luis Sanz Luis Sanz
23 нояб. 2015 г. 13:04:41