
⚙️ 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.php → single.php → singular.php → index.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 3 add_filter( 'template_include', 'sd_cpt_default_template', 99 ); 4 5 function 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 и позволяет переопределить шаблон точечно, без лишних проверок внутри колбэка:
1 add_filter( 'single_template', 'sd_cpt_template_by_type' ); 2 3 function 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: |
Плагин со своим шаблоном | Способ 2: |
Дочерняя тема, нужно переопределить родительский шаблон | Способ 1 или 3 |
FSE / блок-тема (Twenty Twenty-Four и новее) | Способ 4: |
После внедрения любого из методов обязательно зайдите в «Настройки → Постоянные ссылки» и нажмите «Сохранить изменения», это сбрасывает кэш правил перезаписи (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>, и стили подхватятся:
1 add_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:
1 add_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, самый простой и надёжный путь, проверенный годами.



