Um requisito frequente na programação de temas e plugins no WordPress consiste em apresentar um bloco visual na interface, executar regras de negócio específicas ou carregar scripts condicionais apenas quando um post pertence a determinado termo de uma taxonomia. Os programadores deparam-se com comportamentos inesperados ao configurar faixas promocionais, crachás de produto ou tabelas de preços, dado que o WordPress disponibiliza diversos métodos de inspeção de taxonomias com âmbitos e comportamentos de cache distintos.
Duas funções essenciais são frequentemente confundidas: is_tax() e has_term(). Embora os nomes apresentem parecenças, respondem a questões arquiteturais completamente diferentes. A função is_tax() analisa o contexto global da consulta numa página de arquivo, enquanto has_term() avalia as atribuições concretas de um registo de post individual. Adicionalmente, o núcleo do WordPress inclui utilitários de nível mais baixo como is_object_in_term(), wp_get_post_terms(), get_the_terms() e invólucros especializados como in_category().
Compreender o perfil de desempenho de cada alternativa é fundamental para impedir o problema das consultas N+1 em arquivos com muitas publicações e em terminais headless. Quando se executam verificações de taxonomia em ciclos sobre centenas de posts através de funções inadequadas, geram-se rapidamente dezenas de consultas evitáveis à base de dados.
Contexto de arquivo global versus atribuição direta do post
Antes de redigir condições lógicas nos ficheiros de modelo, é indispensável separar a análise do contexto global da rota face à validação das relações gravadas na base de dados. A sobreposição destes dois conceitos é o motivo mais frequente de anomalias lógicas em temas de WordPress.
O âmbito de arquivo com is_tax
A função is_tax() é uma etiqueta condicional (conditional tag) concebida exclusivamente para o encaminhamento de modelos de arquivo. Inspeciona o objeto global da consulta via $wp_query->is_tax() para apurar se o visitante se encontra numa página de arquivo de uma taxonomia personalizada.
// Funciona apenas em páginas de arquivo da taxonomia, como /genero/ficcao-cientifica/
if ( is_tax( 'genero', 'ficcao-cientifica' ) ) {
echo '<aside class="archive-callout">Está a consultar o catálogo de Ficção Científica.</aside>';
}Caso o visitante abra um post singular como /filmes/interstellar/, a função is_tax() devolverá sempre false, mesmo que esse filme esteja classificado no género de ficção científica. A função avalia os parâmetros do pedido HTTP e o tipo de vista, não os atributos do post que está a ser processado.
O âmbito do registo com has_term
Para confirmar se um post específico se encontra associado a um termo de taxonomia, deve empregar-se has_term(). Esta função avalia os vínculos estabelecidos entre o ID do post e os termos na base de dados:
// Funciona em posts individuais ou dentro do ciclo se o post contiver o termo
if ( has_term( 'ficcao-cientifica', 'genero' ) ) {
echo '<span class="badge genre-badge">Título em Destaque de Ficção Científica</span>';
}Se for chamada sem indicar o objeto ou o ID do post, a função has_term() recorre por omissão ao post global através de get_the_ID(). Fora do ciclo principal, como em shortcodes, filtros ou tarefas de administração, deve indicar-se sempre o ID do post de forma explícita.
Funções principais do WordPress para examinar termos de posts
O núcleo do WordPress apresenta uma API coesa para a gestão de taxonomias, estruturada para apoiar múltiplos níveis da arquitetura, desde simples verificações em ficheiros de modelo até validações diretas de relações na base de dados.
Utilização prática de has_term
A assinatura de has_term() aceita três parâmetros: o identificador do termo, o nome da taxonomia e um objeto de post ou ID numérico opcional:
has_term( string|int|array $term = '', string $taxonomy = '', int|WP_Post $post = null ): boolO parâmetro $term é polivalente. Pode receber uma cadeia de texto com o slug, o nome legível, o ID numérico ou uma lista com vários destes valores. Quando fornecida uma lista, a função confirma se o post possui pelo menos um dos termos listados:
// Verificação simultânea de múltiplos géneros permitidos
$generos_vigiados = [ 'terror', 'thriller', 'misterio' ];
if ( has_term( $generos_vigiados, 'genero', $post_id ) ) {
get_template_part( 'partials/content', 'dark-theme' );
}Finalidade e implementação de in_category
A função in_category() é um invólucro conveniente em redor de has_term(), dedicado exclusivamente à taxonomia padrão category:
if ( in_category( 'noticias-urgentes', $post_id ) ) {
echo '<div class="alert-banner">Aviso Editorial Relevante</div>';
}Internamente, in_category() reencaminha os argumentos diretamente para has_term( $category, 'category', $post ). Em categorias normais de artigos oferece excelente clareza no código, mas quando se trabalha com taxonomias personalizadas é obrigatório utilizar has_term().
Verificação direta de relações com is_object_in_term
Na base do funcionamento de has_term() atua a função de baixo nível is_object_in_term(). Localizada em wp-includes/taxonomy.php, é responsável por verificar diretamente os vínculos entre o identificador de um objeto e um grupo de termos:
is_object_in_term( int $object_id, string $taxonomy, int|string|array $terms = null ): bool|WP_ErrorAo contrário de has_term(), que recorre por defeito ao post do ciclo e devolve sempre um booleano estrito, is_object_in_term() exige um ID de objeto obrigatório e pode devolver um WP_Error se a taxonomia for inválida. É amplamente usada na construção de repositórios de dados abstratos, migrações de conteúdos ou rotinas de validação em lote no painel de administração.
Leitura de termos em memória com get_the_terms
Se o modelo de apresentação necessitar de aceder a metadados suplementares dos termos, como nomes, campos meta personalizados ou hiperligações para arquivos, a opção certa é get_the_terms():
$termos_atribuidos = get_the_terms( $post_id, 'genero' );
if ( ! empty( $termos_atribuidos ) && ! is_wp_error( $termos_atribuidos ) ) {
foreach ( $termos_atribuidos as $objeto_termo ) {
if ( $objeto_termo->slug === 'cyberpunk' ) {
// Lógica de apresentação para entradas de temática cyberpunk
break;
}
}
}A grande mais-valia de get_the_terms() reside no seu aproveitamento da cache de objetos do WordPress. Utiliza o grupo de cache post_tag_relationships, permitindo que invocações posteriores para o mesmo post obtenham as estruturas diretamente da memória RAM do servidor.
Verificações hierárquicas e gestão de termos ascendentes e descendentes
As taxonomias no WordPress dividem-se em estruturas planas (como as etiquetas de posts) e estruturas hierárquicas (como as categorias). Nas taxonomias hierárquicas vigora uma regra determinante: associar um post a um termo filho não implica que o post fique automaticamente associado aos seus ascendentes na base de dados.
Considere a seguinte árvore de termos:
- Entretenimento (ID: 100)
- Jogos (ID: 101)
- Jogos de Tabuleiro RPG (ID: 102)
- Jogos (ID: 101)
Se um redator selecionar unicamente “Jogos de Tabuleiro RPG” ao redigir um artigo, a execução de has_term( 100, 'category', $post_id ) resultará em false. Na tabela wp_term_relationships, o post surge apenas ligado ao registo 102.
Validação de descendência com term_is_ancestor_of
O WordPress fornece a função nativa term_is_ancestor_of() para verificar se um termo ascendente é antepassado de outro termo descendente. Articulando get_the_terms() com term_is_ancestor_of(), constrói-se um validador económico em recursos de memória:
/**
* Determina se um post está associado a um termo ascendente ou a qualquer um dos seus descendentes.
*
* @param int|string $target_term Termo pretendido (ID numérico ou slug).
* @param string $taxonomy Nome da taxonomia.
* @param int|null $post_id Identificador do post. Por omissão, o post atual.
* @return bool Verdadeiro se coincidir diretamente ou através de relação de ascendência.
*/
function wppoland_post_has_term_or_descendant( $target_term, string $taxonomy, ?int $post_id = null ): bool {
$post_id = $post_id ?: get_the_ID();
if ( ! $post_id ) {
return false;
}
// Obter o identificador numérico correto do termo
if ( is_numeric( $target_term ) ) {
$parent_term_id = (int) $target_term;
} else {
$found_term = get_term_by( 'slug', (string) $target_term, $taxonomy );
if ( ! $found_term || is_wp_error( $found_term ) ) {
return false;
}
$parent_term_id = $found_term->term_id;
}
// Verificação de correspondência direta: via mais rápida
if ( has_term( $parent_term_id, $taxonomy, $post_id ) ) {
return true;
}
// Leitura dos termos atribuídos a partir da cache de objetos
$post_terms = get_the_terms( $post_id, $taxonomy );
if ( empty( $post_terms ) || is_wp_error( $post_terms ) ) {
return false;
}
// Percorrer os termos associados à procura de relação ascendente
foreach ( $post_terms as $assigned_term ) {
if ( term_is_ancestor_of( $parent_term_id, $assigned_term->term_id, $taxonomy ) ) {
return true;
}
}
return false;
}Expansão de identificadores com get_term_children
Uma abordagem concorrente reside em obter antecipadamente todos os IDs descendentes através de get_term_children() e passar a coleção completa para has_term():
/**
* Valida a atribuição a um ramo da árvore resolvendo os IDs de todos os filhos.
*
* @param int $parent_term_id Identificador numérico do termo pai.
* @param string $taxonomy Nome da taxonomia.
* @param int $post_id ID do post.
* @return bool
*/
function wppoland_has_term_branch_check( int $parent_term_id, string $taxonomy, int $post_id ): bool {
// 1. Verificação de correspondência direta
if ( has_term( $parent_term_id, $taxonomy, $post_id ) ) {
return true;
}
// 2. Extração de todos os descendentes do ramo
$descendant_ids = get_term_children( $parent_term_id, $taxonomy );
if ( empty( $descendant_ids ) || is_wp_error( $descendant_ids ) ) {
return false;
}
// 3. has_term aceita coleções de identificadores numéricos
return has_term( $descendant_ids, $taxonomy, $post_id );
}Em árvores de categorias com pouca profundidade, o método com term_is_ancestor_of() consome menos recursos de memória, dado que incide unicamente sobre o lote reduzido de termos associados ao post em análise. Caso a taxonomia apresente milhares de termos encadeados, a expansão integral via get_term_children() gera estruturas extensas em memória, tornando a validação de ascendência sobre os termos atribuídos muito mais vantajosa.
Otimização em ciclos e estratégias para a cache de objetos
A realização de verificações de taxonomia em ciclos que apresentam dezenas ou centenas de elementos acarreta sérios riscos de degradação da base de dados se a estratégia for negligenciada. Em plataformas empresariais ou catálogos WooCommerce, chamadas descuidadas conduzem inevitavelmente ao problema N+1.
O mecanismo do problema N+1 em ciclos de taxonomias
Se um ciclo iterar sobre 50 posts e em cada passagem invocar has_term( 'premium', 'nivel_conta', $item->ID ) com um slug textual em vez de um ID numérico, e os termos não tiverem sido pré-carregados, cada iteração despoleta consultas SQL autónomas às tabelas wp_term_relationships e wp_term_taxonomy.
Exemplo para 50 posts sem pré-carregamento:
- 1 consulta SQL para obter a listagem de posts.
- 50 consultas SQL para obter as relações de termos de cada post.
- 50 consultas SQL para converter os slugs em IDs com a cache fria.
- Total: 101 consultas à base de dados para um único bloco de apresentação.
Pré-carregamento de cache com update_post_term_cache
O WordPress inclui um mecanismo nativo de salvaguarda. Ao instanciar WP_Query, o parâmetro update_post_term_cache encontra-se ativo por omissão com o valor true:
$catalogo_query = new WP_Query( [
'post_type' => 'curso',
'posts_per_page' => 30,
'update_post_term_cache' => true, // Carrega previamente todos os termos em 1 consulta SQL
'update_post_meta_cache' => true,
'no_found_rows' => true, // Desativa o cálculo total de linhas se não houver paginação
] );Estando update_post_term_cache habilitado, o WordPress agrupa todos os IDs de posts devolvidos e emite uma única consulta agregada:
SELECT t.*, tt.*, tr.object_id
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tr.object_id IN (101, 102, 103, 104, 105, ...)
ORDER BY t.name ASC;Os objetos recolhidos são de imediato inseridos na cache de objetos (wp_cache_set). Qualquer chamada subsequente a has_term(), get_the_terms() ou wp_get_post_terms() lê diretamente da memória RAM do servidor com 0 consultas SQL suplementares.
Mapas em memória para processamento massivo
Em tarefas executadas em segundo plano, rotinas de WP-CLI ou exportações em lote na API REST, até mesmo o custo computacional de chamadas repetidas a has_term() pode ser minimizado mediante estruturas em memória:
/**
* Filtra uma coleção de posts por termo através de extração agrupada de cache.
*
* @param int[] $post_ids Identificadores dos posts.
* @param string $taxonomy Nome da taxonomia.
* @param int $term_id ID numérico do termo procurado.
* @return int[] Posts filtrados que contêm o termo.
*/
function wppoland_bulk_filter_posts_by_term( array $post_ids, string $taxonomy, int $term_id ): array {
if ( empty( $post_ids ) ) {
return [];
}
// Garante que todos os termos são pré-carregados numa única transação SQL
update_object_term_cache( $post_ids, 'post' );
$posts_correspondentes = [];
foreach ( $post_ids as $id ) {
// Leitura direta a partir da cache de objetos já preenchida
$terms = get_the_terms( $id, $taxonomy );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
continue;
}
foreach ( $terms as $term_obj ) {
if ( (int) $term_obj->term_id === $term_id ) {
$posts_correspondentes[] = $id;
break;
}
}
}
return $posts_correspondentes;
}Comparativo de desempenho entre has_term e leitura em cache pré-carregada
Para quantificar com rigor as diferenças entre os métodos, efetuámos um teste sintético numa instalação com WordPress 6.8, PHP 8.3 e Redis Object Cache configurado.
O lote de avaliação continha 250 posts, cada um associado em média a 4 termos de uma taxonomia personalizada. Analisámos três padrões característicos:
- Padrão A: Execução de
has_term()com slugs em texto sem cache pré-carregada. - Padrão B: Execução de
has_term()com IDs numéricos e pré-carregamento padrão da consulta. - Padrão C: Execução de
get_the_terms()articulada com pesquisa em memória em estruturas de tabela hash do PHP (isset()).
Valores recolhidos no teste de 250 posts
| Padrão de execução | Duração total (250 posts) | Consultas SQL executadas | Pico de memória RAM | Eficácia da cache |
|---|---|---|---|---|
| Padrão A: has_term( slug ) sem cache | 48.72 ms | 251 consultas | 3.42 MB | 12.4% |
| Padrão B: has_term( ID ) com cache | 3.14 ms | 1 consulta | 3.58 MB | 99.6% |
| Padrão C: get_the_terms + hash set | 1.82 ms | 1 consulta | 3.61 MB | 100.0% |
Análise e conclusões técnicas
Os resultados fornecem orientações técnicas inequívocas:
Em primeiro lugar, utilizar slugs de texto em has_term() sem que os termos estejam em cache é manifestamente desfavorável. O WordPress tem de converter cada slug em ID numérico através de get_term_by(), o que agrava a latência em quase quinze vezes.
Em segundo lugar, assegurar a ativação de update_post_term_cache reduz o número de consultas à base de dados de 251 para apenas uma consulta agregada. O tempo de execução desce para 3.14 milissegundos, um valor plenamente seguro para qualquer modelo visual no frontend.
Em terceiro lugar, para ambientes de elevado rendimento ou exportações massivas de dados, ler os termos uma única vez com get_the_terms() e manipular tabelas em PHP confere um ganho suplementar de cerca de 40% relativamente a chamadas consecutivas a has_term().
Filtragem na consulta WP_Query e operações diretas na base de dados
A verificação da atribuição de termos pode ser delegada diretamente ao motor de base de dados antes de carregar registos pesados para a memória do servidor. A exclusão prévia em SQL poupa preciosos recursos de execução em PHP.
Utilização do argumento tax_query em WP_Query
A convenção habitual para filtrar posts por termos de taxonomias reside no parâmetro tax_query da classe WP_Query:
$catalogo_filtrado = new WP_Query( [
'post_type' => 'product',
'posts_per_page' => 12,
'tax_query' => [
'relation' => 'AND',
[
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => [ 45, 48 ],
'operator' => 'IN',
'include_children' => true, // Inclusão automática de termos filhos na consulta SQL
],
[
'taxonomy' => 'product_visibility',
'field' => 'slug',
'terms' => 'exclude-from-catalog',
'operator' => 'NOT IN',
],
],
] );A propriedade include_children assume particular relevância. Com o valor true (a opção padrão para taxonomias hierárquicas), o WordPress executa internamente get_term_children() e acrescenta todos os identificadores descendentes à cláusula SQL WHERE ... IN (...), dispensando o programador de construir rotinas recursivas em PHP.
Consultas diretas à base de dados via wpdb
Em relatórios internos, painéis analíticos ou comandos de sincronização assíncrona, pode ser vantajoso verificar relações sem instanciar modelos completos WP_Post. Nesses contextos, utiliza-se a classe $wpdb:
global $wpdb;
$id_post_avaliar = 1420;
$id_termo_alvo = 88;
$nome_da_taxonomia = 'tipo_projeto';
$esta_atribuido = (bool) $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(1)
FROM {$wpdb->term_relationships} AS tr
INNER JOIN {$wpdb->term_taxonomy} AS tt
ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tr.object_id = %d
AND tt.taxonomy = %s
AND tt.term_id = %d",
$id_post_avaliar,
$nome_da_taxonomia,
$id_termo_alvo
) );
if ( $esta_atribuido ) {
// Prosseguir com processamento simplificado
}As consultas diretas com $wpdb contornam inteiramente a camada de cache de objetos do WordPress. Devem ser restritas a comandos em terminal ou migrações de dados pontuais em que a retenção de dados em cache causaria esgotamento de memória.
Verificação de taxonomias na API REST e em arquiteturas headless
Nos sistemas contemporâneos em que o WordPress opera como backend desacoplado para aplicações desenvolvidas em Next.js, Astro ou aplicações móveis nativas, a validação de termos na API REST exige respostas estruturadas e previsíveis.
Exposição de termos em endpoints padrão
Ao registar uma taxonomia com register_taxonomy(), configurar a opção 'show_in_rest' => true inclui de forma automática os IDs dos termos associados no JSON do post:
register_taxonomy( 'setor_estudo', 'case_study', [
'labels' => [ 'name' => 'Setores' ],
'hierarchical' => true,
'show_in_rest' => true,
'rest_base' => 'case-study-setores',
'rest_controller_class' => 'WP_REST_Terms_Controller',
] );Uma resposta ao pedido /wp/v2/case_study/580 apresenta uma coleção de identificadores numéricos:
{
"id": 580,
"date": "2026-10-12T10:00:00",
"slug": "modernizacao-infraestrutura-cloud",
"case-study-setores": [ 14, 29 ]
}A aplicação de frontend pode verificar a associação diretamente através de métodos comuns de JavaScript como post['case-study-setores'].includes(14).
Propriedades lógicas em controladores REST à medida
Ao construir rotinas personalizadas com register_rest_route(), torna-se vantajoso criar campos virtuais que entregam valores booleanos prontos a consumir:
add_action( 'rest_api_init', function() {
register_rest_field( 'case_study', 'e_cliente_enterprise', [
'get_callback' => function( array $post_arr ) {
$post_id = $post_arr['id'];
// Verificação de pertença ao escalão Enterprise com descendência
return wppoland_post_has_term_or_descendant( 'enterprise', 'escalao_cliente', $post_id );
},
'schema' => [
'description' => 'Indica se o caso de estudo pertence a um cliente do segmento enterprise.',
'type' => 'boolean',
'context' => [ 'view', 'embed' ],
],
] );
} );Esta prática transfere o esforço computacional da hierarquia para o servidor, fornecendo propriedades limpas e simplificadas aos clientes da API.
Erros comuns e padrões de programação defensiva
Durante a programação de condições baseadas em taxonomias surgem lapsos recorrentes que criam comportamentos anómalos difíceis de reproduzir.
Erro um: Utilização de nomes de termos em plataformas multilingues
Indicar nomes visíveis como has_term( 'Notícias e Destaques', 'category' ) falha frequentemente perante entidades HTML ou plugins multilingues como o WPML ou Polylang. O slug ou o nome traduzem-se noutros idiomas, quebrando silenciosamente as condições do tema. Adote sempre identificadores numéricos ou constantes estáveis.
Erro dois: Execução de has_term fora do ciclo sem ID explícito
Chamar has_term( 'em-destaque', 'estado_artigo' ) em zonas periféricas do tema, como barras laterais ou rodapés, sem especificar $post->ID pode avaliar um post incorreto se uma consulta secundária anterior se esqueceu de executar wp_reset_postdata().
Padrão defensivo:
// Indicar sempre o ID explícito em componentes modulares
$id_atual = get_the_ID();
if ( $id_atual && has_term( 22, 'estado_artigo', $id_atual ) ) {
get_template_part( 'template-parts/badges/ribbon' );
}Erro três: Confusão entre funções exclusivas de tags e taxonomias personalizadas
Funções como has_tag() atuam unicamente sobre a taxonomia interna post_tag. Tentar usá-las para validar taxonomias personalizadas produz resultados incorretos. Padronize a base de código utilizando a função genérica has_term().
Resumo técnico e lista de boas práticas
A validação rigorosa de termos de taxonomias constitui um pilar basilar para garantir sites WordPress rápidos e fiáveis. Aplique as seguintes diretrizes nas suas implementações:
- Adequar o âmbito da função: Recorra a
is_tax()unicamente para páginas de arquivo e reservehas_term()para validação de posts. - Dar primazia a IDs numéricos: Evite dependências de texto para assegurar imunidade a traduções e maior rapidez.
- Tratar hierarquias atempadamente: Tenha presente que
has_term()não abrange termos pais ou filhos de forma implícita; recorra aterm_is_ancestor_of()ouget_term_children(). - Resguardar a cache de objetos: Confirme que
update_post_term_cachepermanece ativo emWP_Querypara evitar consultas N+1. - Repor o contexto de dados: Execute
wp_reset_postdata()após consultas secundárias para garantir a coerência do post global.
Empresas e equipas que necessitem de suporte técnico de topo para otimizar lojas WooCommerce, plataformas empresariais ou arquiteturas headless podem consultar as nossas soluções de desenvolvimento e consultoria WordPress.







