Skip to content

Всё для WordPress, веб-разработки — и не только

⚙️ WordPress: как установить шаблон по умолчанию для кастомного типа записей

⚙️ WordPress: как установить шаблон по умолчанию для кастомного типа записей

Сдали клиенту сайт на WordPress с портфолио, а он звонит через день: «Я тут запись добавляю, а шаблон каждый раз слетает на стандартный с сайдбаром. Как его закрепить?» Ситуация до боли знакомая: клик по «Добавить новую» в кастомном типе, и вместо сверстанной страницы на всю ширину выезжает блоговая колонка с сайдбаром. Выбирать шаблон вручную при каждой публикации, бессмысленная рутина, которая раздражает и вас, и заказчика. Особенно когда практически всегда нужен «Full Width» без боковой панели.

WordPress по умолчанию для кастомных типов записей (CPT) берёт single.php из темы. А single.php обычно заточен под блог: заголовок, контент, сайдбар с виджетами. Для портфолио, кейсов, отзывов или товаров такая верстка не подходит. Хорошая новость: WordPress предлагает четыре способа закрепить шаблон за CPT, от самого простого (файл в теме, никакого кода) до полностью программного через хуки и блок-темы. Каждый метод решает задачу надёжно, разница лишь в уровне контроля и удобстве поддержки.

💡 Быстрый обзор:

  • Создайте файл single-{post_type}.php в корне темы, WordPress подхватит его автоматически по иерархии шаблонов
  • Подключите фильтр template_include в functions.php, задайте шаблон программно без создания физического файла в теме
  • Для блок-тем создайте single-{post_type}.html в папке templates/, редактируется через Site Editor без кода
  • После любого изменения сбросьте пермалинки: «Настройки → Постоянные ссылки → Сохранить», иначе WordPress не увидит новый шаблон

Способ 1: Файл шаблона по иерархии WordPress

Самый надёжный и простой метод. WordPress с версии 3.0 поддерживает кастомные шаблоны для CPT через стандартную иерархию шаблонов. Работает это так: когда посетитель открывает запись типа portfolio, WordPress ищет шаблон в строго определённом порядке, сверху вниз, и использует первый найденный файл:

single-portfolio.phpsingle.phpsingular.phpindex.php

Вам нужно создать файл с именем single-{post_type}.php, где {post_type}, слаг вашего кастомного типа. Положите его в корень активной темы:

1/* Файл: /wp-content/themes/your-theme/single-portfolio.php */
2
3<?php get_header(); ?>
4
5<div class="full-width-content">
6 <?php while ( have_posts() ) : the_post(); ?>
7 <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
8 <h1><?php the_title(); ?></h1>
9 <div class="entry-content">
10 <?php the_content(); ?>
11 </div>
12 </article>
13 <?php endwhile; ?>
14</div>
15
16<?php get_footer(); ?>
17

Самый быстрый старт, скопируйте содержимое page.php или template-fullwidth.php из вашей темы и адаптируйте вёрстку под конкретный CPT. Если файла single-{post_type}.php нет, WordPress молча откатывается на single.php, и клиент получает сайдбар.

Плюс метода, никакого кода в functions.php, файл просто лежит в теме и работает. Минус, шаблон жёстко привязан к теме: при смене темы файл остаётся в старой папке и перестаёт работать.

Способ 2: Программно через фильтр template_include

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

Фильтр срабатывает перед загрузкой шаблона и позволяет указать PHP-файл из любого места:

1/* Файл: functions.php (темы) или основной файл плагина */
2
3add_filter( 'template_include', 'sd_cpt_default_template', 99 );
4
5function sd_cpt_default_template( $template ) {
6 if ( is_singular( 'portfolio' ) ) {
7 $custom_template = plugin_dir_path( __FILE__ ) . 'templates/single-portfolio.php';
8 if ( file_exists( $custom_template ) ) {
9 return $custom_template;
10 }
11 }
12 return $template;
13}

Что здесь происходит:

  • is_singular('portfolio') проверяет, что мы на странице одиночной записи типа portfolio
  • Если условие истинно, WordPress загружает шаблон из папки плагина, игнорируя иерархию темы
  • Приоритет 99 гарантирует, что фильтр сработает последним и не будет перебит темой

Этот подход удобен, когда шаблон должен жить внутри плагина и переживать смены темы. Файл templates/single-portfolio.php внутри плагина может использовать get_header() и get_footer() из активной темы, визуально всё останется в дизайне сайта.

Способ 3: Фильтр {$type}_template

WordPress предоставляет динамический хук {$type}_template, в котором $type это single, archive или page. Для CPT он принимает вид single-portfolio_template и позволяет переопределить шаблон точечно, без лишних проверок внутри колбэка:

1add_filter( 'single_template', 'sd_cpt_template_by_type' );
2
3function sd_cpt_template_by_type( $single_template ) {
4 global $post;
5
6 if ( 'portfolio' === $post->post_type ) {
7 $custom_template = get_stylesheet_directory() . '/single-portfolio.php';
8 if ( file_exists( $custom_template ) ) {
9 return $custom_template;
10 }
11 }
12 return $single_template;
13}

Разница со Способом 2, семантическая: вы явно говорите «меняем шаблон для сингл-записей» вместо «перехватываем вообще все шаблоны». Код чуть чище, но функционально оба хука решают задачу одинаково.

Способ 4: Шаблон в блок-теме (Full Site Editing)

С WordPress 5.9 и переходом на блок-темы подход меняется. В FSE-темах шаблоны, это HTML-файлы в папке templates/, а не PHP. Для кастомного типа записей создайте:

1/wp-content/themes/your-fse-theme/templates/single-portfolio.html

Внутри, структура на блоках. Минимальный пример:

1<!-- wp:template-part {"slug":"header","theme":"your-fse-theme"} /-->
2
3<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
4<main class="wp-block-group">
5 <!-- wp:post-title {"level":1} /-->
6 <!-- wp:post-featured-image /-->
7 <!-- wp:post-content {"layout":{"type":"constrained"}} /-->
8</main>
9<!-- /wp:group -->
10
11<!-- wp:template-part {"slug":"footer","theme":"your-fse-theme"} /-->

Редактировать такой шаблон можно прямо в Site Editor (Внешний вид → Редактор), без касания кода. При смене блок-темы файл остаётся в старой папке, здесь та же уязвимость, что и у Способа 1.

Какой способ выбрать

Короткая матрица под ваш сценарий:

Ситуация

Способ

Одна тема, один CPT, без плагинов

Способ 1: single-{post_type}.php

Плагин со своим шаблоном

Способ 2: template_include

Дочерняя тема, нужно переопределить родительский шаблон

Способ 1 или 3

FSE / блок-тема (Twenty Twenty-Four и новее)

Способ 4: templates/single-{post_type}.html

После внедрения любого из методов обязательно зайдите в «Настройки → Постоянные ссылки» и нажмите «Сохранить изменения», это сбрасывает кэш правил перезаписи (rewrite rules), и WordPress начинает видеть новый шаблон.

⁉️🤔 Частые вопросы

Шаблон не подхватывается, что делать?

Первое: сбросьте пермалинки («Настройки → Постоянные ссылки → Сохранить»). Второе: проверьте, что слаг CPT в имени файла совпадает со слагом из register_post_type(). Третье: если используется кэширующий плагин, очистите кэш. По опыту, проблема чаще всего именно в сбросе rewrite rules после регистрации CPT, а не в коде шаблона.

Можно ли задать один шаблон сразу для нескольких CPT?

Да. В Способе 2 используйте массив в is_singular(): is_singular( array( 'portfolio', 'testimonials', 'team' ) ). Или проверяйте in_array( $post->post_type, array('portfolio', 'team') ) внутри колбэка. Файл шаблона при этом один, вёрстка будет общей для всех перечисленных типов.

Шаблон работает, но верстка ломается, нет стилей сайдбара/хедера.

Вы унаследовали шаблон от page.php, а стили темы завязаны на CSS-классы body. Добавьте фильтр body_class в functions.php, чтобы WordPress проставлял класс post-type-{slug} на <body>, и стили подхватятся:

1add_filter( 'body_class', function( $classes ) {
2 if ( is_singular( 'portfolio' ) ) {
3 $classes[] = 'single-portfolio';
4 }
5 return $classes;
6} );

Что делать при смене темы, шаблон пропадает?

Да, файл single-portfolio.php остаётся в папке старой темы. Варианты: перенесите файл в новую тему вручную, используйте дочернюю тему (файл переживает обновления родительской) или вынесите шаблон в плагин через Способ 2, он не зависит от активной темы.

А если мне нужно, чтобы ВСЕ CPT по умолчанию открывались без сайдбара?

Используйте универсальный фильтр с проверкой is_singular() без указания конкретного post_type, но исключите стандартные post и page:

1add_filter( 'template_include', function( $template ) {
2 if ( is_singular() && ! is_singular( array( 'post', 'page' ) ) ) {
3 $fullwidth = get_stylesheet_directory() . '/template-fullwidth.php';
4 if ( file_exists( $fullwidth ) ) {
5 return $fullwidth;
6 }
7 }
8 return $template;
9}, 99 );

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

Стоит ли заморачиваться с template_include, если можно просто создать файл?

Если у вас один сайт, одна тема и один CPT, создайте single-{post_type}.php и забудьте. Это решение на годы: оно не ломается при обновлении WordPress, не зависит от версии PHP и понятно любому разработчику, который откроет папку темы. Сопровождение сводится к одному файлу, который можно поправить за минуту.

Программные методы через хуки оправданы в двух случаях. Первый: шаблон, часть распространяемого плагина, и вы не хотите требовать от пользователя копировать файлы в тему. Второй: CPT регистрируются динамически через ACF, Toolset или подобный плагин, и плодить десяток single-*.php в теме неудобно. Для всего остального иерархия шаблонов WordPress, самый простой и надёжный путь, проверенный годами.