the_category() imprime âncoras HTML. Se o cartão do arquivo, o slider ou a badge do tema precisam de texto puro, essa função está no caminho errado. A alternativa correta é get_the_category() (ou get_the_terms() para taxonomias genéricas): devolve objetos PHP, e decide se o nome transforma-se em <span>, classe CSS ou string para um atributo aria-label.
Este tutorial cobre o contrato de saída de the_category(), a forma do array de get_the_category(), escape com esc_html(), listas com implode(), o antipadrão strip_tags( the_category() ), get_the_terms(), categoria primária (Yoast / Rank Math), Full Site Editing e o papel da object cache.
Porque the_category mostra a categoria com link
No Codex e no Developer Reference, the_category() é uma template tag de saída: escreve HTML no buffer. O separador entre categorias é um argumento de string, mas cada nome continua dentro de <a href="...">. Não existe parâmetro oficial para «só o nome».
<?php
// No Loop: imprime HTML com links para os arquivos de categoria.
the_category( ', ' );
?>Exemplo típico de markup gerado (simplificado):
<a href="https://exemplo.pt/categoria/noticias/" rel="category tag">Notícias</a>,
<a href="https://exemplo.pt/categoria/eventos/" rel="category tag">Eventos</a>Para um badge visual no content-card.php, esse markup traz problemas concretos:
- o clique na badge compete com o clique no título do post;
- estilos de
ado tema (underline, cor de foco) herdam-se para a label; - leitores de ecrã anunciam «link» quando a intenção era só um rótulo;
- em JSON-LD ou meta tags, o HTML fica exposto se alguém copiar a saída sem sanitizar.
Quando o desenho pede texto, leia dados. Quando o desenho pede navegação para o arquivo, aí sim the_category() ou um <a> construído à mão com get_category_link() fazem sentido.
O que devolve get_the_category no WordPress
get_the_category( $post_id = false ) devolve um array de objetos WP_Term (historicamente documentados como objetos de categoria). Sem argumentos, usa o post global do Loop. Com ID, funciona fora do Loop, por exemplo num shortcode ou num callback REST.
Propriedades usadas na prática do tema:
| Propriedade | Uso típico |
|---|---|
name | Texto visível da badge |
slug | Classe CSS (badge-noticias) |
term_id | Chaves de cache, comparações |
term_taxonomy_id | Identidade na tabela term_taxonomy |
description | Tooltips raros; costuma ser HTML |
count | Contagem de posts na categoria |
parent | Hierarquia; 0 = raiz |
cat_ID | Alias legado de term_id |
Snippet mínimo para a primeira categoria:
<?php
$categories = get_the_category();
if ( ! empty( $categories ) ) {
echo esc_html( $categories[0]->name );
}
?>O índice [0] é a primeira categoria na ordem em que o WordPress as devolve para aquele post. Essa ordem não é «categoria primária SEO». Se o editor atribuiu três categorias e Yoast marcou outra como primary, $categories[0] pode ser a errada para o breadcrumb comercial. A secção sobre categoria primária cobre esse caso.
Posts sem categorias (ou tipos de conteúdo onde a taxonomia category não está registada) devolvem array vazio. O ! empty() evita avisos de índice indefinido em PHP 8+.
Como escapar o nome da categoria com esc_html
Nomes de categoria são conteúdo editorial. Um editor com capacidade manage_categories pode meter <script> ou aspas no nome. Em HTML de texto, use esc_html(). Em atributos (class, data-*, aria-label), use esc_attr().
<?php
$categories = get_the_category();
$first = ! empty( $categories ) ? $categories[0] : null;
if ( $first ) :
?>
<span
class="badge badge-<?php echo esc_attr( $first->slug ); ?>"
aria-label="<?php echo esc_attr( sprintf( 'Categoria: %s', $first->name ) ); ?>"
>
<?php echo esc_html( $first->name ); ?>
</span>
<?php
endif;Diferença prática:
esc_html()converte<,>,&, aspas para entidades adequadas a nós de texto;esc_attr()trata o contexto de atributo (aspas e caracteres que partem o markup);sanitize_html_class( $first->slug )é útil se o slug puder ter caracteres estranhos após migrações antigas.
Não escape duas vezes. esc_html( esc_html( $name ) ) não «segura mais»; só dificulta a leitura e pode alterar entidades em cenários edge.
Como listar várias categorias sem link com implode
Vários nomes sem âncoras: acumule strings escapadas e junte com implode().
<?php
$categories = get_the_category();
$names = array();
if ( ! empty( $categories ) ) {
foreach ( $categories as $category ) {
$names[] = esc_html( $category->name );
}
echo implode( ', ', $names );
}
?>Vantagens face a concatenar à mão com $out .= $name . ', ':
- o separador fica num sítio só;
- não sobra vírgula final;
- pode trocar
', 'por' · 'ou por'</span><span class="sep">'sem reescrever o loop.
Se precisar de markup por item (cada nome num <span>), faça o escape dentro do fragmento e junte os fragmentos:
<?php
$categories = get_the_category();
$chips = array();
foreach ( (array) $categories as $category ) {
$chips[] = sprintf(
'<span class="chip chip-%1$s">%2$s</span>',
esc_attr( $category->slug ),
esc_html( $category->name )
);
}
echo implode( ' ', $chips );
?>Aqui implode junta HTML já escapado por peça. Não passe o HTML completo por esc_html() no fim - isso tornaria as tags visíveis como texto.
Porque não usar strip_tags em the_category?
Ainda aparece em temas antigos e em respostas de fóruns:
<?php
// Antipadrão: não faças isto.
echo strip_tags( get_the_category_list( ', ' ) );
// ou:
ob_start();
the_category( ', ' );
echo strip_tags( ob_get_clean() );
?>Problemas:
- Custo desnecessário. Constrói HTML completo (links,
rel, URLs) para deitar fora as tags. - Separadores frágeis. Se o tema ou um filtro em
the_categoryinjeta wrappers,strip_tagspode colar nomes sem espaço (NotíciasEventos). - Escape errado. Depois de
strip_tags, ainda precisa deesc_html(); muita gente esquece e imprime texto cru. - Filtros laterais. Plugins que alteram o HTML de
the_categorypassam a afetar «texto puro» sem o programador perceber. - Testes. Unit tests sobre strings HTML são mais ruidosos do que asserts sobre
$term->name.
O caminho curto e estável é get_the_category() + propriedade name. Reserve strip_tags para conteúdo rico onde realmente recebeu HTML de origem desconhecida - não para remover âncoras que o próprio pediu à API.
Como obter termos de taxonomia personalizada com get_the_terms
get_the_category() é um atalho para a taxonomia category em posts. Em custom post types com taxonomias próprias (setor, tipo_projeto, mercado), use get_the_terms( $post, $taxonomy ).
<?php
$terms = get_the_terms( get_the_ID(), 'setor' );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return;
}
$names = array();
foreach ( $terms as $term ) {
$names[] = esc_html( $term->name );
}
echo implode( ', ', $names );
?>Notas de contrato:
- retorno pode ser
false, array deWP_Term, ouWP_Errorse a taxonomia não existir; - verifique sempre
is_wp_error()antes de iterar; - a forma do objeto é a mesma família (
name,slug,term_id); - para um único termo «como categoria principal do CPT», a mesma lógica de primary (meta do SEO plugin) aplica-se se o plugin a suportar nessa taxonomia.
wp_get_post_terms() é irmão próximo com argumentos de WP_Term_Query. Para «nomes no template», get_the_terms() costuma bastar e beneficia da cache de termos do post.
Obter a categoria primária do Yoast e Rank Math
Em arquivos e cartões de blog corporativo, o negócio quer uma label: a categoria comercial, não a primeira da lista alfabética ou a ordem de atribuição no editor.
Yoast SEO grava o ID em post meta _yoast_wpseo_primary_category. Rank Math usa rank_math_primary_category. Sem plugin, defina a sua própria meta ou assuma $categories[0] de forma documentada no README do tema.
<?php
/**
* Devolve o WP_Term da categoria primária, ou o primeiro de get_the_category().
*/
function wppoland_get_primary_category( $post_id = 0 ) {
$post_id = $post_id ? (int) $post_id : get_the_ID();
$primary_id = (int) get_post_meta( $post_id, '_yoast_wpseo_primary_category', true );
if ( ! $primary_id ) {
$primary_id = (int) get_post_meta( $post_id, 'rank_math_primary_category', true );
}
if ( $primary_id ) {
$term = get_term( $primary_id, 'category' );
if ( $term && ! is_wp_error( $term ) ) {
return $term;
}
}
$categories = get_the_category( $post_id );
return ! empty( $categories ) ? $categories[0] : null;
}
$primary = wppoland_get_primary_category();
if ( $primary ) {
echo esc_html( $primary->name );
}
?>Regras de robustez:
- se o termo primary foi apagado mas a meta ficou, faça fallback para
get_the_category(); - não faça cache do nome em transient sem invalidar quando o post ou o termo mudam;
- no breadcrumb, a primary é a que deve alimentar o item intermédio entre Home e o post.
Nome da categoria sem link no bloco Post Terms
Em temas de blocos (FSE), o bloco Post Categories / Post Terms renderiza links por omissão, alinhado com the_category(). Para texto puro no editor de site dispõe de três vias honestas:
- CSS only (limitado).
pointer-events: noneetext-decoration: noneno bloco de categorias do cartão. Remove o gesto de clique visualmente, mas o markup continua a ser<a>- mau para acessibilidade e para «copiar como texto». - Bloco personalizado ou variação. Um bloco dinâmico PHP que chama
get_the_category()e imprime<span>. Registe comblock.jsonerender_callback. - Filtro no HTML do bloco.
render_blockpode reescrever o HTML do core, mas é frágil entre versões do WordPress. Prefira um bloco seu se o cartão for peça central do design system.
Exemplo mínimo de render_callback para um bloco de tema:
<?php
function wppoland_render_category_label_block( $attributes, $content, $block ) {
$post_id = isset( $block->context['postId'] )
? (int) $block->context['postId']
: get_the_ID();
$categories = get_the_category( $post_id );
if ( empty( $categories ) ) {
return '';
}
$term = $categories[0];
return sprintf(
'<span class="wp-block-wppoland-category-label">%s</span>',
esc_html( $term->name )
);
}
?>No Query Loop, o contexto postId do bloco é o que garante a categoria do item certo - não dependa só do Loop global se o callback correr em contextos aninhados.
Como fazer cache do nome da categoria no WordPress
get_the_category() e get_the_terms() leem a relação post-termo e os objetos WP_Term. Com object cache persistente (Redis, Memcached via drop-in), termos e meta entram em cache de forma transparente. Na prática:
- Não reinvente um transient por cartão só para o nome da categoria no Loop principal do arquivo. O custo de
get_the_category()no Loop é baixo face ao resto do template. - Caches de fragmento HTML (cartões inteiros em transient ou em edge) devem incluir o nome da categoria dentro do HTML cacheado, e invalidar quando o post é guardado (
save_post) ou quando o termo é editado (edited_term). - Se guarda só o
term_idno seu índice custom (Elasticsearch, tabela própria), resolva onamena renderização comget_term()- assim renomear a categoria atualiza o arquivo sem rebuild do índice de IDs.
Exemplo de invalidação ao guardar o post, se mantém um fragmento por ID:
<?php
add_action(
'save_post_post',
function ( $post_id ) {
delete_transient( 'wppoland_card_html_' . (int) $post_id );
}
);
?>Evite cachear a string já passada por esc_html() durante horas se editores alteram nomes com frequência em sites de notícias - ou aceite um TTL curto (5-15 minutos) e documente o compromisso.
Checklist para mostrar categorias sem link no tema
Antes de fechar o PR do partial template-parts/card-category.php:
- Confirmou que o design quer texto, não navegação para o arquivo.
- Usa
get_the_category()ouget_the_terms(), nuncastrip_tagsem cima dethe_category(). - Todo o
namevisível passa poresc_html(); slugs em classes poresc_attr()ousanitize_html_class(). - Decidiu explicitamente entre «primeira do array» e «categoria primária SEO».
- No FSE, o cartão não depende de
pointer-events: nonecomo solução permanente. - Fragmentos HTML cacheados invalidam em
save_post/ edição de termo.
Código PHP da categoria sem link no cartão de arquivo
<?php
/**
* template-parts/card-category-label.php
*
* @package WPPoland_Theme
*/
defined( 'ABSPATH' ) || exit;
$post_id = get_the_ID();
$primary = function_exists( 'wppoland_get_primary_category' )
? wppoland_get_primary_category( $post_id )
: null;
if ( ! $primary ) {
$categories = get_the_category( $post_id );
$primary = ! empty( $categories ) ? $categories[0] : null;
}
if ( ! $primary ) {
return;
}
?>
<span class="card__category card__category--<?php echo esc_attr( $primary->slug ); ?>">
<?php echo esc_html( $primary->name ); ?>
</span>Este partial separa dados de apresentação: o CSS pinta a badge; o PHP não emite âncoras. Se mais tarde o produto pedir link, envolva o esc_html( $primary->name ) num <a href="<?php echo esc_url( get_category_link( $primary->term_id ) ); ?>"> - sem reintroduzir the_category().
Quando usar the_category com links?
Há ecrãs em que o HTML com links é o produto:
- rodapé do single clássico «Publicado em Notícias, Eventos»;
- widgets de «meta do post» onde cada categoria deve ser clicável;
- temas minimalistas sem cartões, onde o arquivo por categoria é a navegação secundária.
Nesses casos chame the_category( ', ' ) (ou o bloco Post Terms) e não reinvente o markup. O erro é misturar os dois modos no mesmo componente sem regra: badge do cartão com texto, meta do single com links - documente a regra no tema para o próximo programador não «unificar» tudo com strip_tags.
Funções WordPress para obter categorias
| Função | Papel | Saída |
|---|---|---|
the_category() | Template tag de impressão | HTML com <a> |
get_the_category() | Dados da taxonomia category | WP_Term[] |
get_the_category_list() | Lista HTML configurável | string HTML |
get_the_terms() | Qualquer taxonomia | WP_Term[]|false|WP_Error |
get_category_link() | URL do arquivo | string URL |
Documentação oficial (Developer Reference):
Erros comuns ao mostrar categorias no tema
Em code review, procure estes sinais de regressão:
echo $category->namesem escape;the_category()dentro de atributos HTML (title="");- dependência da ordem do array quando o negócio fala em «categoria principal»;
- testes E2E que assertam href na badge do cartão (o teste fica a vermelho quando passa a texto puro - atualize o seletor para
.card__categorysema).
Em sites multilingues (WPML, Polylang), o name já vem na língua do termo traduzido se o post estiver na língua certa. Não concatene nomes de outro idioma a partir de IDs sem passar pela API do plugin de tradução.
Se precisar de quem implemente estes partials e a regra de primary no seu tema, fale com a equipa de desenvolvimento WordPress.
Última verificação editorial deste guia: 21 de setembro de 2026. As funções citadas mantêm o mesmo contrato público no Developer Handbook; valide sempre contra a versão do WordPress do projeto antes de copiar snippets para produção.







