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

supertrue
источник
Никогда раньше не использовал такую ​​настройку, но если она слишком сложная, почему бы просто не создать файл шаблона и связать его с рассматриваемым обзором.
Шейн
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 );
?>
skladany
источник
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;
}
отметка
источник