Lógica condicional WordPress para categorias e taxonomias

Lógica condicional WordPress para categorias e taxonomias

Última verificação: 21 de setembro de 2026
11 min de leitura
Guia
Desenvolvedor full-stack

A lógica condicional no WordPress não é um conjunto de if espalhados pelo functions.php. É um contrato com a query principal: o núcleo decide o contexto da pedido HTTP, as conditional tags leem esse contexto, e o seu código reage. Em 2026, com temas de blocos e Full Site Editing (FSE), o PHP continua a governar assets, capabilities, cache e qualquer ramo que o editor de sítio não consegue expressar.

Este guia trata a lógica condicional como engenharia: quando is_singular() mente, quando in_the_loop() falha fora do Loop, porque current_user_can() e page cache não se misturam, e como isolar helpers que dá para testar. A taxonomia (in_category vs has_term) entra no fim como caso concreto, não como único tema.

#De onde as conditional tags leem o estado

As tags condicionais não inspeccionam o URL à mão. Depois de parse_request e WP_Query, o objeto global $wp_query guarda flags (is_single, is_page, is_front_page, is_category, …). Funções como is_singular() e is_front_page() são wrappers sobre essas flags.

Consequência prática: se chamar uma conditional tag demasiado cedo - antes de wp / template_redirect - o resultado é indefinido ou falso. Em plugins, o sítio seguro para ramos de template e enqueue é wp_enqueue_scripts, template_redirect ou hooks posteriores documentados em Hooks.

Outra consequência: uma WP_Query secundária (widget, bloco de posts recentes, shortcode) não altera as flags da query principal. is_singular() continua a descrever a página pedida, não o post que o widget está a iterar. Para dados de um post específico, passe o ID ou o objeto WP_Post às APIs de post/termo.

#Erros comuns com is_singular e is_front_page

is_singular() devolve verdadeiro para um post, página ou CPT singular. Não distingue tipo. Em lojas WooCommerce, is_singular() é verdadeiro em product, o que leva muita gente a carregar CSS de blog no PDP. Prefira is_singular( 'post' ), is_page(), ou is_singular( array( 'post', 'page' ) ) com a lista explícita.

is_front_page() e is_home() continuam a ser a confusão clássica:

  • Com «As suas últimas publicações» em Definições > Leitura, a homepage é is_front_page() e is_home().
  • Com uma página estática como página inicial e outra como «página de publicações», a página inicial é só is_front_page(); o feed de posts é is_home() e não is_front_page().

Código que faz if ( is_home() ) para «mostrar o hero da homepage» parte quando o cliente muda a leitura para página estática. O padrão estável para a homepage pública é is_front_page(). Para o arquivo do blog, use is_home() && ! is_front_page().

Caso de produção visto em Gdynia: um tema carregava o slider só com is_home(). Depois de migrar a homepage para uma página com blocos, o slider desapareceu e o arquivo /blog/ herdou o JavaScript do carrossel. Uma linha - is_front_page() - teria bastado.

is_singular() também falha em endpoints que parecem «uma página» ao utilizador mas não são singular no query: pesquisa (is_search()), 404 (is_404()), alguns endpoints REST e previews. Antes de assumir «há um post atual», confirme get_queried_object() e o tipo.

#Como usar in_the_loop e has_term fora do Loop

Funções como the_title(), the_content() e várias condicionais de post (in_category() sem ID) dependem do post global definido pelo Loop. in_the_loop() diz se o código corre entre while ( have_posts() ) : the_post(); e o fim desse Loop na query em causa.

Fora do Loop - num header.php, num filtro body_class, num wp_footer - in_category( 'noticias' ) sem terceiro argumento lê o post global, que pode ser o último da query anterior, um post de um widget, ou null. Sintoma típico: a classe CSS «categoria notícias» aparece no arquivo, ou o aviso legal só no último artigo da listagem.

Padrão seguro:

function wppoland_is_news_singular(): bool {
	if ( ! is_singular( 'post' ) ) {
		return false;
	}

	$post_id = (int) get_queried_object_id();
	if ( $post_id <= 0 ) {
		return false;
	}

	return has_term( 'noticias', 'category', $post_id );
}

Use get_queried_object_id() para o documento da pedido; use o ID do post dentro de um Loop secundário quando estiver a iterar resultados. Não misture os dois.

in_the_loop() ajuda a decidir se pode confiar no post global. Se estiver fora do Loop e precisar de dados do documento atual, não «entre» no Loop artificialmente só para ler meta: leia pelo ID da query principal.

#current_user_can e ramos de autenticação

current_user_can( 'edit_posts' ) e afins leem a sessão e as capabilities do utilizador atual. São a API correta para UI administrativa, atalhos de edição e conteúdo só para editores.

Três regras que evitam incidentes:

  1. Nunca misture HTML diferente para anónimos e autenticados no mesmo documento se esse documento for servido por page cache partilhado. O edge ou o plugin de cache guarda a variante do primeiro visitante. Um editor vê o botão «Editar»; o próximo anónimo recebe o mesmo HTML.
  2. Para ramos autenticados, use cookies de bypass de cache (padrão da maioria dos plugins de page cache), ESI, ou sirva o ramo só via JavaScript/REST autenticado depois do HTML estático.
  3. Capabilities não são roles. Teste current_user_can( 'manage_options' ), não current_user_can( 'administrator' ) - o segundo não é o contrato estável da API.

Em temas clássicos, um padrão frequente é esconder a admin bar para subscritores. Faça-o com um filtro em show_admin_bar, não com CSS condicional no HTML em cache:

add_filter(
	'show_admin_bar',
	static function ( $show ) {
		if ( is_user_logged_in() && ! current_user_can( 'edit_posts' ) ) {
			return false;
		}
		return $show;
	}
);

O filtro corre no pedido PHP; o page cache anónimo nunca o vê porque o utilizador autenticado já está fora desse cache.

#Quando usar a hierarquia de templates e quando condicionais PHP

A hierarquia de templates resolve ficheiros (single-product.php, category-noticias.php, front-page.php). As conditional tags resolvem ramos dentro de um ficheiro partilhado (header.php, functions.php, um partial).

Prefira a hierarquia quando o markup diverge de forma estrutural: um CPT com layout próprio merece single-{post_type}.php (ou o equivalente em block theme: template HTML dedicado). Prefira condicionais PHP quando a diferença é pequena: um aviso, um enqueue, uma classe no body.

Anti-padrão: um único page.php genérico com vinte if ( is_page( 'contacto' ) ) encadeados. Isso duplica a hierarquia em PHP, dificulta code review e impede que o FSE (ou um child theme) substitua só um template. Extraia páginas com markup distinto para templates próprios; deixe condicionais para variação fina.

Regra de ouro: se o ramo cabe num nome de ficheiro da hierarquia, use o ficheiro. Se o ramo depende de capability, cookie, A/B ou feature flag, use PHP (ou um bloco com lógica no servidor via plugin).

#Quais são os limites do FSE em temas de blocos?

O FSE move templates e partes para HTML no editor de sítio. Não elimina conditional tags.

O editor de sítio cobre variação visual por tipo de template (singular, arquivo, 404). Não cobre, de forma fiável:

  • Enqueue condicional de scripts pesados (mapas, calculadoras, players).
  • Ramos por capability (current_user_can).
  • Lógica de taxonomia recursiva (pai inclui filhos).
  • Integrações ERP/WooCommerce que dependem de meta do produto na query atual.
  • Qualquer coisa que precise de WP_Query secundária com argumentos dinâmicos.

Block themes ainda carregam functions.php. É aí que vivem wp_enqueue_scripts, filtros de body_class, e helpers de termo. Os blocos com «variantes» no editor não substituem has_term() quando a regra de negócio é «se o produto está em qualquer descendente de roupa».

Limite operacional: markup só no editor, decisões de runtime no PHP. Misturar as duas camadas sem contrato claro (por exemplo, um padrão de bloco que assume is_front_page() e um plugin que altera a query principal) produz regressões difíceis de reproduzir em staging.

#Taxonomias: in_category versus has_term

in_category() é um atalho histórico para a taxonomia category. Não cobre product_cat, post_tag nem taxonomias registadas com register_taxonomy().

has_term( $term, $taxonomy, $post = null ) é a API geral. Para código novo, prefira has_term() mesmo em categorias nativas: a assinatura é explícita e o mesmo helper serve WooCommerce e CPTs.

Verificação nativa é correspondência exata. Um post só em maca não satisfaz in_category( 'fruta' ) só porque maca é filha de fruta. O core não oferece «está neste ramo da árvore» numa sola chamada. Use get_term_children() (e object cache) num helper:

function wppoland_post_in_term_branch( int $post_id, string $term_slug, string $taxonomy ): bool {
	$term = get_term_by( 'slug', $term_slug, $taxonomy );
	if ( ! $term instanceof WP_Term ) {
		return false;
	}

	if ( has_term( (int) $term->term_id, $taxonomy, $post_id ) ) {
		return true;
	}

	$children = get_term_children( (int) $term->term_id, $taxonomy );
	if ( is_wp_error( $children ) || $children === [] ) {
		return false;
	}

	return has_term( $children, $taxonomy, $post_id );
}

Armadilha WooCommerce: is_product_category() descreve o arquivo de categoria de produto, não o produto no Loop. Dentro de um loop de produtos use has_term( $slug, 'product_cat', $post_id ).

#Page cache com utilizadores autenticados no WordPress

Page cache (nginx fastcgi_cache, Cloudflare, plugins de full-page cache) serve HTML pronto a anónimos. Qualquer if ( is_user_logged_in() ) ou current_user_can() embutido nesse HTML é uma vulnerabilidade de vazamento de UI ou, no sentido inverso, de conteúdo em falta.

Padrões que funcionam:

  • Separar: HTML público idêntico para todos os anónimos; UI autenticada via endpoint autenticado ou fragmento sem cache.
  • Bypass: cookies de sessão fazem o proxy saltar o cache para utilizadores autenticados (custo: menos hit ratio).
  • Não condicionar assets críticos da homepage com is_user_logged_in() no PHP do documento em cache.

Condicionais «seguras» sob page cache anónimo: is_front_page(), is_singular( 'post' ), has_term( …, $queried_id ) - tudo o que depende só da URL/query pública. Condicionais «perigosas» no mesmo documento: capabilities, preferências de utilizador, geolocalização por IP sem vary, A/B sem cookie de vary.

Em projetos com edge cache na UE, documente a matriz vary (cookie, header) ao lado do helper. Sem isso, o próximo plugin de «barra de promo para clientes» volta a partilhar HTML.

#Como testar conditional tags com PHPUnit

Condicionais inline em templates são difíceis de testar. Extraia predicados puros (ou quase puros) que recebem estado explícito:

function wppoland_should_load_map_assets( bool $is_contact_page, bool $user_can_edit ): bool {
	return $is_contact_page || $user_can_edit;
}

No tema:

add_action(
	'wp_enqueue_scripts',
	static function () {
		$should = wppoland_should_load_map_assets(
			is_page( 'contacto' ),
			current_user_can( 'edit_pages' )
		);
		if ( ! $should ) {
			return;
		}
		wp_enqueue_script( 'wppoland-maps', /* … */ );
	}
);

Em PHPUnit (com WordPress test suite ou doubles), testa wppoland_should_load_map_assets( true, false ) sem bootstrap de $wp_query. O mesmo padrão aplica-se a ramos de taxonomia: passe $post_id e slugs, não leia globais dentro do predicado.

Convenções úteis:

  • Prefixo de projeto no nome (wppoland_…) para evitar colisões.
  • Retorno bool explícito; sem eco de HTML dentro do helper.
  • Um helper = uma pergunta de negócio («é singular de notícias?»), não um switch com side effects.

#Desempenho de has_term e get_term_children

has_term() e conditional tags sobre a query principal são baratas quando o object cache está quente. get_term_children() em taxonomias profundas pode custar; memorize o resultado por request (static local ou wp_cache_get / wp_cache_set com grupo próprio).

Evite chamar o mesmo predicado dezenas de vezes num Loop: calcule uma vez antes do Loop ou cache por $post_id. Em arquivos com 30 produtos e verificação recursiva de product_cat, a diferença nota-se em TTFB em hosting partilhado.

Não use SQL cru para «atalhos» de categoria se has_term() chega: perde object cache e preparações do $wpdb que o core já faz.

#Lista de verificação para lógica condicional no WordPress

  1. A conditional tag corre depois de a query principal estar pronta?
  2. is_front_page() vs is_home() está alinhado com Definições → Leitura?
  3. Termos e categorias usam ID de post explícito fora do Loop?
  4. Algum ramo depende de current_user_can() no HTML em page cache?
  5. A divergência de markup cabe na hierarquia de templates em vez de um if gigante?
  6. O helper tem testes ou, no mínimo, casos manuais documentados (anónimo, editor, arquivo, singular)?

#Resumo

Conditional tags leem a query principal; trate-as como API de contexto, não como parsing de URL. Separe hierarquia de templates (estrutura) de PHP condicional (variação fina e runtime). No FSE, o editor cobre templates; o PHP continua a cobrir assets, capabilities e regras de taxonomia. has_term() substitui in_category() em código novo; ramos autenticados ficam fora do HTML anónimo em cache. Helpers com argumentos explícitos são a forma de manter a lógica revisável e testável.

Lógica condicional mal colocada num template é o género de erro que só aparece no post que ninguém testou. Se quiser esses templates revistos por quem já apanhou o erro antes, veja a nossa prática de desenvolvimento WordPress.

Próximo passo

Transforme o artigo numa implementação real

Este bloco reforça a ligação interna e conduz o leitor para o passo seguinte mais útil dentro da arquitetura do site.

Quer implementar isto no seu site?

Se quer transformar o artigo em melhorias concretas, redesign ou num plano de implementação, posso fechar o escopo e executar.

Cluster relacionado

Explorar outros serviços WordPress e base de conhecimento

Reforce o seu negócio com suporte técnico profissional em áreas-chave do ecossistema WordPress.

FAQ do artigo

Perguntas frequentes

Respostas práticas para aplicar o tema na execução real.

SEO-readyGEO-readyAEO-ready3 Q&A
Quando devo usar has_term em vez de in_category?#
Use has_term quando precisa de suportar taxonomias personalizadas ou quando quer uma abordagem mais flexível do que a categoria nativa.
Como verificar categorias filhas no WordPress?#
Normalmente precisa de uma função auxiliar com get_term_children, porque a verificação nativa não resolve bem relações recursivas.
A lógica condicional pode afetar performance?#
Pode, sobretudo em loops grandes e taxonomias extensas. Vale a pena reduzir chamadas repetidas e confiar em object cache sempre que possível.

Precisa de FAQ adaptado ao setor e mercado? Criamos uma versão alinhada com os seus objetivos de negócio.

Fale connosco

Artigos Relacionados