
⚙️ WordPress: jak ustawić domyślny szablon dla niestandardowego typu wpisów
Oddaliśmy klientowi stronę na WordPress z portfolio, a on dzwoni następnego dnia: „Dodaję wpis, a szablon za każdym razem przeskakuje na domyślny z paskiem bocznym. Jak go przypisać na stałe?" Sytuacja boleśnie znajoma: kliknięcie „Dodaj nowy" w niestandardowym typie treści i zamiast zakodowanej strony na całą szerokość wyjeżdża kolumna blogowa z sidebar. Ręczne wybieranie szablonu przy każdej publikacji to bezsensowna rutyna, która irytuje zarówno Pana/Panią, jak i zleceniodawcę. Zwłaszcza gdy praktycznie zawsze potrzebny jest „Full Width" bez panelu bocznego.
WordPress domyślnie dla niestandardowych typów wpisów (CPT) pobiera single.php z motywu. A single.php jest zwykle dostosowany do bloga: nagłówek, treść, sidebar z widżetami. Dla portfolio, case studies, referencji czy produktów taki układ nie pasuje. Dobra wiadomość: WordPress oferuje cztery sposoby na przypisanie szablonu do CPT na stałe, od najprostszego (plik w motywie, zero kodu) po w pełni programowy przez hooki i motywy blokowe. Każda metoda rozwiązuje zadanie niezawodnie, różnica polega jedynie na poziomie kontroli i wygodzie utrzymania.
💡 Szybki przegląd:
- Proszę utworzyć plik
single-{post_type}.phpw katalogu głównym motywu, WordPress przechwyci go automatycznie zgodnie z hierarchią szablonów - Proszę podpiąć filtr
template_includewfunctions.php, ustawić szablon programowo bez tworzenia fizycznego pliku w motywie - Dla motywów blokowych proszę utworzyć
single-{post_type}.htmlw folderzetemplates/, edytuje się przez Site Editor bez kodu - Po każdej zmianie proszę zresetować bezpośrednie odnośniki: „Ustawienia → Bezpośrednie odnośniki → Zapisz", w przeciwnym razie WordPress nie zobaczy nowego szablonu
Sposób 1: Plik szablonu według hierarchii WordPress
Najbardziej niezawodna i najprostsza metoda. WordPress od wersji 3.0 obsługuje niestandardowe szablony dla CPT poprzez standardową hierarchię szablonów. Działa to tak: gdy odwiedzający otwiera wpis typu portfolio, WordPress szuka szablonu w ściśle określonej kolejności, od góry do dołu, i używa pierwszego znalezionego pliku:
single-portfolio.php → single.php → singular.php → index.php
Należy utworzyć plik o nazwie single-{post_type}.php, gdzie {post_type} to slug Pana/Pani niestandardowego typu. Proszę umieścić go w katalogu głównym aktywnego motywu:
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
Najszybszy start: proszę skopiować zawartość page.php lub template-fullwidth.php z Pana/Pani motywu i dostosować układ do konkretnego CPT. Jeśli plik single-{post_type}.php nie istnieje, WordPress po cichu przełącza się na single.php i klient otrzymuje sidebar.
Zaleta metody: zero kodu w functions.php, plik po prostu leży w motywie i działa. Wada: szablon jest sztywno powiązany z motywem, przy zmianie motywu plik pozostaje w starym folderze i przestaje działać.
Sposób 2: Programowo przez filtr template_include
Metoda plikowa działa, dopóki kontroluje Pan/Pani motyw. Jeśli jednak tworzy Pan/Pani wtyczkę lub motyw potomny, który nie powinien zależeć od obecności pliku w katalogu głównym, potrzebny jest hook template_include.
Filtr uruchamia się przed załadowaniem szablonu i pozwala wskazać plik PHP z dowolnego miejsca:
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 }
Co tu się dzieje:
is_singular('portfolio')sprawdza, czy jesteśmy na stronie pojedynczego wpisu typuportfolio- Jeśli warunek jest prawdziwy, WordPress ładuje szablon z folderu wtyczki, ignorując hierarchię motywu
- Priorytet 99 gwarantuje, że filtr zadziała jako ostatni i nie zostanie nadpisany przez motyw
To podejście jest wygodne, gdy szablon ma żyć wewnątrz wtyczki i przetrwać zmiany motywu. Plik templates/single-portfolio.php wewnątrz wtyczki może używać get_header() i get_footer() z aktywnego motywu, wizualnie wszystko pozostanie w designie strony.
Sposób 3: Filtr {$type}_template
WordPress udostępnia dynamiczny hook {$type}_template, w którym $type to single, archive lub page. Dla CPT przyjmuje on postać single-portfolio_template i pozwala nadpisać szablon punktowo, bez zbędnych sprawdzeń wewnątrz callbacka:
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 }
Różnica w stosunku do Sposobu 2 jest semantyczna: jawnie mówi Pan/Pani „zmieniamy szablon dla pojedynczych wpisów" zamiast „przechwytujemy wszystkie szablony". Kod jest nieco czystszy, ale funkcjonalnie oba hooki rozwiązują zadanie tak samo.
Sposób 4: Szablon w motywie blokowym (Full Site Editing)
Od WordPress 5.9 i przejścia na motywy blokowe podejście się zmienia. W motywach FSE szablony to pliki HTML w folderze templates/, a nie PHP. Dla niestandardowego typu wpisów proszę utworzyć:
1 /wp-content/themes/your-fse-theme/templates/single-portfolio.html
Wewnątrz znajduje się struktura oparta na blokach. Minimalny przykład:
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"} /-->
Taki szablon można edytować bezpośrednio w Site Editor (Wygląd → Edytor), bez dotykania kodu. Przy zmianie motywu blokowego plik pozostaje w starym folderze, tutaj występuje ta sama podatność co w Sposobie 1.
Który sposób wybrać
Krótka matryca dla Pana/Pani scenariusza:
Sytuacja | Sposób |
|---|---|
Jeden motyw, jeden CPT, bez wtyczek | Sposób 1: |
Wtyczka z własnym szablonem | Sposób 2: |
Motyw potomny, potrzeba nadpisania szablonu rodzica | Sposób 1 lub 3 |
FSE / motyw blokowy (Twenty Twenty-Four i nowsze) | Sposób 4: |
Po wdrożeniu dowolnej z metod koniecznie proszę wejść w „Ustawienia → Bezpośrednie odnośniki" i kliknąć „Zapisz zmiany", resetuje to cache reguł przepisywania (rewrite rules) i WordPress zaczyna widzieć nowy szablon.
⁉️🤔 Często zadawane pytania
Szablon nie jest przechwytywany, co robić?
Po pierwsze: proszę zresetować bezpośrednie odnośniki („Ustawienia → Bezpośrednie odnośniki → Zapisz"). Po drugie: proszę sprawdzić, czy slug CPT w nazwie pliku pokrywa się ze slugiem z
register_post_type(). Po trzecie: jeśli używana jest wtyczka cache, proszę wyczyścić cache. Z doświadczenia, problem najczęściej leży właśnie w resecie rewrite rules po rejestracji CPT, a nie w kodzie szablonu.
Czy można ustawić jeden szablon dla kilku CPT jednocześnie?
Tak. W Sposobie 2 proszę użyć tablicy w
is_singular():is_singular( array( 'portfolio', 'testimonials', 'team' ) ). Albo proszę sprawdzićin_array( $post->post_type, array('portfolio', 'team') )wewnątrz callbacka. Plik szablonu jest wtedy jeden, układ będzie wspólny dla wszystkich wymienionych typów.
Szablon działa, ale układ się psuje, brakuje stylów sidebara/headera.
Odziedziczył Pan/Pani szablon z
page.php, a style motywu są powiązane z klasami CSSbody. Proszę dodać filtrbody_classwfunctions.php, aby WordPress dodawał klasępost-type-{slug}do<body>i style zostaną przechwycone:
1 add_filter( 'body_class', function( $classes ) { 2 if ( is_singular( 'portfolio' ) ) { 3 $classes[] = 'single-portfolio'; 4 } 5 return $classes; 6 } );
Co robić przy zmianie motywu, szablon znika?
Tak, plik
single-portfolio.phppozostaje w folderze starego motywu. Opcje: proszę przenieść plik do nowego motywu ręcznie, użyć motywu potomnego (plik przetrwa aktualizacje rodzica) lub wynieść szablon do wtyczki przez Sposób 2, nie zależy on od aktywnego motywu.
A co jeśli potrzebuję, aby WSZYSTKIE CPT domyślnie otwierały się bez sidebara?
Proszę użyć uniwersalnego filtra ze sprawdzeniem
is_singular()bez podawania konkretnego post_type, ale z wykluczeniem standardowychpostipage:
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 );
To rozwiązanie stosuje jeden szablon full-width dla wszystkich niestandardowych typów wpisów jednocześnie: portfolio, referencji, zespołu, case studies. Standardowe posty i strony nie są ruszane.
Czy warto zawracać sobie głowę template_include, skoro można po prostu utworzyć plik?
Jeśli ma Pan/Pani jedną stronę, jeden motyw i jeden CPT, proszę utworzyć single-{post_type}.php i zapomnieć. To rozwiązanie na lata: nie psuje się przy aktualizacji WordPress, nie zależy od wersji PHP i jest zrozumiałe dla każdego developera, który otworzy folder motywu. Utrzymanie sprowadza się do jednego pliku, który można poprawić w minutę.
Metody programowe przez hooki są uzasadnione w dwóch przypadkach. Pierwszy: szablon jest częścią dystrybuowanej wtyczki i nie chce Pan/Pani wymagać od użytkownika kopiowania plików do motywu. Drugi: CPT są rejestrowane dynamicznie przez ACF, Toolset lub podobną wtyczkę i tworzenie dziesiątek single-*.php w motywie jest niewygodne. Do wszystkiego innego hierarchia szablonów WordPress to najprostsza i najbardziej niezawodna ścieżka, sprawdzona latami.



