
⚙️ WordPress: como definir um modelo padrão para um tipo de publicação personalizado
Entregou ao cliente um site em WordPress com um portefólio e, no dia seguinte, ele telefona: «Estou a adicionar uma nova entrada, mas o modelo está sempre a reverter para o predefinido com uma barra lateral. Como é que o fixo?» A situação é dolorosamente familiar: clica em «Adicionar Novo» num tipo de conteúdo personalizado e, em vez do layout de largura total que construiu, aparece uma coluna de blogue com uma barra lateral. Escolher o modelo manualmente para cada publicação é um trabalho inútil que frustra tanto o programador como o cliente. Especialmente quando «Largura Total» sem barra lateral é o que precisa quase sempre.
Por predefinição, o WordPress usa o ficheiro single.php do tema para os tipos de conteúdo personalizados (CPT). E o single.php costuma ser pensado para um blogue: título, conteúdo, barra lateral com widgets. Para portefólios, casos de estudo, testemunhos ou produtos, esse layout é um mau ajuste. A boa notícia: o WordPress oferece quatro formas de atribuir um modelo a um CPT, desde a mais simples (um ficheiro no tema, sem código nenhum) até à totalmente programática através de hooks e temas de blocos. Cada método resolve o problema de forma fiável; a única diferença é o nível de controlo e a facilidade de manutenção.
💡 Visão geral rápida:
- Crie um ficheiro
single-{post_type}.phpna raiz do tema e o WordPress irá buscá-lo automaticamente através da hierarquia de modelos - Use o hook no filtro
template_includenofunctions.phppara atribuir um modelo programaticamente sem colocar um ficheiro físico no tema - Para temas de blocos, crie
single-{post_type}.htmlna pastatemplates/; pode ser editado através do Editor do Site sem código - Após qualquer alteração, limpe os permalinks: «Definições → Permalinks → Guardar alterações», caso contrário o WordPress não verá o novo modelo
Método 1: ficheiro de modelo através da hierarquia do WordPress
O método mais fiável e direto. Desde a versão 3.0, o WordPress suporta modelos personalizados para CPTs através da hierarquia de modelos padrão. Funciona assim: quando um visitante abre uma entrada do tipo portfolio, o WordPress procura um modelo numa ordem rigorosa, de cima para baixo, e usa o primeiro ficheiro que encontrar:
single-portfolio.php → single.php → singular.php → index.php
Precisa de criar um ficheiro com o nome single-{post_type}.php, em que {post_type} é o slug do seu tipo personalizado. Coloque-o na raiz do tema ativo:
1 /* File: /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
A forma mais rápida de começar: copie o conteúdo de page.php ou template-fullwidth.php do seu tema e adapte o layout para o CPT específico. Se não existir nenhum ficheiro single-{post_type}.php, o WordPress recorre silenciosamente ao single.php e o cliente acaba com uma barra lateral.
A vantagem deste método: nenhum código no functions.php; o ficheiro fica simplesmente no tema e funciona. A desvantagem: o modelo fica fortemente acoplado ao tema. Se mudar de tema, o ficheiro permanece na pasta antiga e deixa de funcionar.
Método 2: programaticamente através do filtro template_include
O método baseado em ficheiros funciona desde que controle o tema. Mas se estiver a desenvolver um plugin ou um tema filho que não deve depender de um ficheiro presente na raiz, precisa do hook template_include.
O filtro é executado antes de o modelo carregar e permite-lhe apontar para um ficheiro PHP a partir de qualquer localização:
1 /* File: functions.php (theme) or main plugin file */ 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 }
O que está a acontecer aqui:
is_singular('portfolio')verifica se estamos numa página de entrada única do tipoportfolio- Se a condição for verdadeira, o WordPress carrega o modelo da pasta do plugin, ignorando a hierarquia do tema
- A prioridade 99 garante que o filtro é executado em último lugar e não é substituído pelo tema
Esta abordagem é conveniente quando o modelo precisa de residir dentro de um plugin e sobreviver a mudanças de tema. O ficheiro templates/single-portfolio.php dentro do plugin pode usar get_header() e get_footer() do tema ativo, pelo que visualmente tudo se mantém consistente com o design do site.
Método 3: o filtro {$type}_template
O WordPress fornece um hook dinâmico {$type}_template, em que $type é single, archive ou page. Para um CPT assume a forma single-portfolio_template e permite-lhe substituir o modelo com precisão, sem verificações extra dentro da função de callback:
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 }
A diferença em relação ao Método 2 é semântica: está explicitamente a dizer «altera o modelo para entradas únicas» em vez de «interceta todos os modelos». O código é ligeiramente mais limpo, mas funcionalmente ambos os hooks resolvem o problema da mesma forma.
Método 4: modelo num tema de blocos (Full Site Editing)
A partir do WordPress 5.9 e da mudança para temas de blocos, a abordagem altera-se. Nos temas FSE, os modelos são ficheiros HTML na pasta templates/, não PHP. Para um tipo de conteúdo personalizado, crie:
1 /wp-content/themes/your-fse-theme/templates/single-portfolio.html
No interior, usa uma estrutura baseada em blocos. Um exemplo mínimo:
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"} /-->
Pode editar este modelo diretamente no Editor do Site (Aparência → Editor) sem tocar em código. Se mudar de tema de blocos, o ficheiro permanece na pasta antiga, a mesma vulnerabilidade do Método 1.
Qual o método a escolher
Uma matriz de decisão rápida para o seu cenário:
Cenário | Método |
|---|---|
Um tema, um CPT, sem plugins | Método 1: |
Plugin com o seu próprio modelo | Método 2: |
Tema filho, precisa de substituir o modelo do tema pai | Método 1 ou 3 |
FSE / tema de blocos (Twenty Twenty-Four e mais recentes) | Método 4: |
Depois de implementar qualquer um dos métodos, não se esqueça de ir a «Definições → Permalinks» e clicar em «Guardar alterações». Isto limpa a cache das regras de reescrita e o WordPress começa a reconhecer o novo modelo.
⁉️🤔 Perguntas frequentes
O modelo não está a ser detetado. O que devo fazer?
Primeiro, limpe os permalinks («Definições → Permalinks → Guardar»). Segundo, verifique se o slug do CPT no nome do ficheiro corresponde ao slug de
register_post_type(). Terceiro, se houver um plugin de cache ativo, limpe a cache. Na prática, o problema está mais frequentemente relacionado com a limpeza das regras de reescrita após registar o CPT, e não com o código do modelo em si.
Posso atribuir um modelo a vários CPTs ao mesmo tempo?
Sim. No Método 2, use um array em
is_singular():is_singular( array( 'portfolio', 'testimonials', 'team' ) ). Ou verifiquein_array( $post->post_type, array('portfolio', 'team') )dentro da função de callback. O ficheiro de modelo é um só e o layout será partilhado por todos os tipos listados.
O modelo funciona, mas o layout está quebrado (faltam estilos da barra lateral/cabeçalho).
Herdou o modelo do
page.php, mas os estilos do tema dependem de classes CSS nobody. Adicione um filtrobody_classnofunctions.phppara que o WordPress aplique uma classepost-type-{slug}ao<body>e os estilos serão aplicados:
1 add_filter( 'body_class', function( $classes ) { 2 if ( is_singular( 'portfolio' ) ) { 3 $classes[] = 'single-portfolio'; 4 } 5 return $classes; 6 } );
O que acontece quando mudo de tema? O modelo desaparece.
Sim, o ficheiro
single-portfolio.phppermanece na pasta do tema antigo. As suas opções: mova o ficheiro manualmente para o novo tema, use um tema filho (o ficheiro sobrevive a atualizações do tema pai) ou mova o modelo para um plugin através do Método 2, que não depende do tema ativo.
E se eu precisar que TODOS os CPTs abram sem barra lateral por predefinição?
Use um filtro universal com
is_singular()sem especificar um post_type em particular, mas exclua os tipos padrãopostepage:
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 );
Esta solução aplica um único modelo de largura total a todos os tipos de conteúdo personalizados de uma só vez: portefólios, testemunhos, membros da equipa, casos de estudo. As publicações e páginas padrão não são afetadas.
Vale a pena incomodar-se com o template_include quando se pode simplesmente criar um ficheiro?
Se tem um site, um tema e um CPT, crie single-{post_type}.php e esqueça o assunto. Esta é uma solução que dura anos: não se parte com atualizações do WordPress, não depende da versão do PHP e é imediatamente clara para qualquer programador que abra a pasta do tema. A manutenção resume-se a um único ficheiro que pode corrigir num instante.
Os métodos programáticos através de hooks justificam-se em dois casos. Primeiro: o modelo faz parte de um plugin distribuído e não quer obrigar os utilizadores a copiar ficheiros para o tema. Segundo: os CPTs são registados dinamicamente através de ACF, Toolset ou um plugin semelhante, e proliferar uma dúzia de ficheiros single-*.php no tema é impraticável. Para tudo o resto, a hierarquia de modelos do WordPress é o caminho mais simples e fiável, comprovado ao longo de anos.



