
⚙️ 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, найпростіший і найнадійніший шлях, перевірений роками.



