single - {$ post_type} - {slug} .php для пользовательских типов записей


20

Моя любимая часть иерархии шаблонов Wordpress - возможность быстро создавать файлы шаблонов для страниц с помощью slug, без необходимости редактировать страницу в Wordpress, чтобы выбрать шаблон.

В настоящее время мы можем сделать это:

PAGE- {пробкового} .php

Но я хотел бы иметь возможность сделать это:

одно- {post_type} - {пробкового} .php

Так, например, в типе сообщения под названием reviewя мог бы создать шаблон для сообщения под названием «Мой большой обзор» вsingle-review-my-great-review.php

Кто-нибудь настраивал это раньше? single-{post_type}-{slug}.php


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

WP 3.4 автоматически загружается single-{post_type}-{slug}.php, поэтому обновление до WP 3.4 - это еще один вариант.
— Yitwail

Ответы:


19

А) База в ядре

Как вы можете видеть в объяснении иерархии шаблонов Кодекса , single-{$post_type}.phpуже поддерживается.


Б) Расширение основной иерархии

Теперь внутри есть несколько фильтров и крючков /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • AND: определенный фильтр внутри с get_query_template( $type, ... )именем:"$type}_template"

Б.1) Как это работает

  1. Внутри файла загрузчика шаблона шаблон загружается условным запросом var / wp_query: is_*() .
  2. Условное затем срабатывает (в случае «одного» шаблона): is_single() && $template = get_single_template()
  3. Это вызывает тогда get_query_template( $type, $templates ), где$type находитсяsingle
  4. Тогда у нас есть "{$type}_template"фильтр

В) решение

Как мы только хотим расширить иерархию с одним шаблоном , который получает загружен до фактического "single-{$object->post_type}.php"шаблона, мы будем перехватывать иерархию и добавить новый шаблон к началу массива шаблонов.

// Extend the hierarchy
function add_posttype_slug_template( $templates )
{

    $object = get_queried_object();

    // New 
    $templates[] = "single-{$object->post_type}-{$object->post_name}.php";
    // Like in core
    $templates[] = "single-{$object->post_type}.php";
    $templates[] = "single.php";

    return locate_template( $templates );    
}
// Now we add the filter to the appropriate hook
function intercept_template_hierarchy()
{
    add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );

ПРИМЕЧАНИЕ: (Если вы хотите использовать что-то другое, чем слаг объектов по умолчанию), вам нужно будет настроить его в $slugсоответствии с вашей структурой постоянных ссылок. Просто используйте все, что вам нужно от глобального (object) $post.

Билеты на Trac

Поскольку вышеприведенный подход в настоящее время не поддерживается (таким образом вы можете фильтровать только абсолютный путь), вот список билетов trac:


Я хочу проверить это, но похоже, что в конце строки add_filter чего-то не хватает.
— SuperTrue

@supertrue Хороший улов. :) Найден еще один пропавший )внутри фильтра. Исправлена. Может быть, вы хотите поменять тире подчеркиванием перед слагом внутри шаблона. Просто чтобы суффикс лучше выделялся при просмотре шаблонов.
— Кайзер

Вызывает эту ошибку по всему сайту: Предупреждение: array_unshift () [function.array-unshift]: первый аргумент должен быть массивом в [строке, содержащей array_unshift]
— supertrue

Хорошо, но тогда что-то еще перехватывает основные шаблоны. Функция отлично работает и $templatesпредставляет собой массив. См. Основные функции в этом файле (без даты истечения срока действия). Убедитесь, что вы проверили это с установкой без плагинов и темой по умолчанию. Затем активируйте один за другим и посмотрите, не возникла ли ошибка.
— Кайзер

Да, я отладил это, и я получил окончательный абсолютный путь первого найденного шаблона обратно в виде строки. Я должен поговорить об этом с разработчиком ядра, прежде чем менять ответ. Также: я кое-что перепутал: slugдоступно только для терминов и таксономий. Вы должны заменить $post->post_nameто, что соответствует вашей структуре постоянных ссылок. В настоящее время нет способа автоматически сделать это для всех случаев с извлечением и заменой пути в зависимости от вашей структуры perma и правил перезаписи. Ожидайте другое обновление.
— Кайзер

4

Следуя образу иерархии шаблонов я не вижу такой опции.

Вот как я бы это сделал:

Решение 1 (Лучшее на мой взгляд)

Создайте файл шаблона и свяжите его с рецензией

 <?php
 /*
 Template Name: My Great Review
 */
 ?>

При добавлении php-файла шаблона в каталог вашей темы он будет отображаться как шаблон на странице редактирования вашего сообщения.

Решение 2

Вероятно, это может быть достигнуто с помощью template_redirectкрючка.

В файле functions.php:

 function my_redirect()
 {
      global $post;

      if( get_post_type( $post ) == "my_cpt" && is_single() )
      {
           if( file_exists( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' ) )
           {
                include( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' );
                exit;
           }
      }
 }
 add_action( 'template_redirect', 'my_redirect' );

РЕДАКТИРОВАТЬ

Добавлена file_existsпроверка


Почему ты exit;там?
— Кайзер

@kaiser Должно быть, в любом уроке, которым я следовал в то время, если это не нужно, я его удалю.
— Шейн

1
@kaiser: exit()необходим для предотвращения загрузки шаблона по умолчанию.
— scribu

Решение 1 будет работать только для страниц, а не сообщений.
— IXN

2

Верхний ответ (от 4 лет назад) больше не работает, но кодекс WordPress имеет решение здесь :

<?php
function add_posttype_slug_template( $single_template )
{
    $object = get_queried_object();
    $single_postType_postName_template = locate_template("single-{$object->post_type}-{$object->post_name}.php");
    if( file_exists( $single_postType_postName_template ) )
    {
        return $single_postType_postName_template;
    } else {
        return $single_template;
    }
}
add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
?>

1

Использовать шаблоны страниц

Другой подход к масштабируемости - дублирование раскрывающейся функциональности шаблона страницы в pageтипе записи для вашего пользовательского типа записи.

Код многократного использования

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

Код

# Define your custom post type string
define('MY_CUSTOM_POST_TYPE', 'my-cpt');

/**
 * Register the meta box
 */
add_action('add_meta_boxes', 'page_templates_dropdown_metabox');
function page_templates_dropdown_metabox(){
    add_meta_box(
        MY_CUSTOM_POST_TYPE.'-page-template',
        __('Template', 'rainbow'),
        'render_page_template_dropdown_metabox',
        MY_CUSTOM_POST_TYPE,
        'side', #I prefer placement under the post actions meta box
        'low'
    );
}

/**
 * Render your metabox - This code is similar to what is rendered on the page post type
 * @return void
 */
function render_page_template_dropdown_metabox(){
    global $post;
    $template = get_post_meta($post->ID, '_wp_page_template', true);
    echo "
        <label class='screen-reader-text' for='page_template'>Page Template</label>
            <select name='_wp_page_template' id='page_template'>
            <option value='default'>Default Template</option>";
            page_template_dropdown($template);
    echo "</select>";
}

/**
 * Save the page template
 * @return void
 */
function save_page_template($post_id){

    # Skip the auto saves
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;
    elseif ( defined( 'DOING_AJAX' ) && DOING_AJAX )
        return;
    elseif ( defined( 'DOING_CRON' ) && DOING_CRON )
        return;

    # Only update the page template meta if we are on our specific post type
    elseif(MY_CUSTOM_POST_TYPE === $_POST['post_type'])
        update_post_meta($post_id, '_wp_page_template', esc_attr($_POST['_wp_page_template']));
}
add_action('save_post', 'save_page_template');


/**
 * Set the page template
 * @param string $template The determined template from the WordPress brain
 * @return string $template Full path to predefined or custom page template
 */
function set_page_template($template){
    global $post;
    if(MY_CUSTOM_POST_TYPE === $post->post_type){
        $custom_template = get_post_meta($post->ID, '_wp_page_template', true);
        if($custom_template)
            #since our dropdown only gives the basename, use the locate_template() function to easily find the full path
            return locate_template($custom_template);
    }
    return $template;
}
add_filter('single_template', 'set_page_template');

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


1

В моем случае у меня есть пользовательские типы записей Альбом и Трек, связанные таксономией Альбома. Я хотел использовать разные одиночные шаблоны для записей альбома и трека в зависимости от их таксономии в альбоме.

Основываясь на ответе Кайзера выше, я написал этот код. Это работает хорошо.
Заметка. Мне не нужно было add_action ().

// Add an additional template option to the template hierarchy
add_filter( 'single_template', 'add_albumtrack_taxslug_template', 10, 1 );
function add_albumtrack_taxslug_template( $orig_template_path )
{
    // at this point, $orig_template_path is an absolute located path to the preferred single template.

    $object = get_queried_object();

    if ( ! (
        // specify another template option only for Album and Track post types.
        in_array( $object->post_type, array( 'gregory-cpt-album','gregory-cpt-track' )) &&
        // check that the Album taxonomy has been registered.
        taxonomy_exists( 'gregory-tax-album' ) &&
        // get the Album taxonomy term for the current post.
        $album_tax = wp_get_object_terms( $object->ID, 'gregory-tax-album' )
        ))
        return $orig_template_path;

    // assemble template name
    // assumption: only one Album taxonomy term per post. we use the first object in the array.
    $template = "single-{$object->post_type}-{$album_tax[0]->slug}.php";
    $template = locate_template( $template );
    return ( !empty( $template ) ? $template : $orig_template_path );
}

Теперь я могу создавать шаблоны с именами single-gregory-cpt-track-tax-serendipity.php и single-gregory-cpt-album-tax-serendipity.php, и WP будет использовать их автоматически; 'tax-serendipity' - это термин для первого термина таксономии Альбома.

для справки: хук фильтра 'single_template' объявлен в:
/wp-includes/theme.php:get_query_template()

Спасибо Кайзер за пример кода.

Ура, Грегори


Привет Грег - добро пожаловать в WPSE. Пожалуйста, публикуйте ответы только как ответы на вопросы, но не задавайте вопросы. Если у вас есть вопрос, на который нет существующего ответа и который слишком велик для комментария, пожалуйста, откройте другой вопрос :)
— Стивен Харрис

1
вопрос о строке / массиве был удален :-)
— Gregory

1
«Спасибо, Кайзер, за образец кода». - Пожалуйста.
— Кайзер

Работает ли это для вас? во-первых, «$ template» не следует комментировать в вашем коде ... и я думаю, что вместо «$ album_tax [0] -> slug» должен быть «$ object-> post_name», не так ли?
— gregmatys

исправлена ​​строка шаблона $. Спасибо. $ object-> POST_NAME? нет. это вернуло бы слизня поста, но мне нужен кусок альбома, с которым связан пост.
— Григорий

0

Обновив код Brians, я обнаружил, что когда раскрывающийся список не использовался, опция шаблона «по умолчанию» сохранялась в wp_page_template, в результате чего он пытался найти шаблон с именем default. это изменение просто проверяет опцию «по умолчанию» при сохранении и вместо этого удаляет метаданные поста (полезно, если вы изменили опцию шаблона обратно на значение по умолчанию)

elseif (MY_CUSTOM_POST_TYPE === $ _POST ['post_type']) {

if (esc_attr ($ _ POST ['_ wp_page_template']) === "default"):
    delete_post_meta ($ post_id, '_wp_page_template');
еще:
    update_post_meta ($ post_id, '_wp_page_template', esc_attr ($ _ POST ['_ wp_page_template']));
ENDIF;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.