Comprobar si una entrada pertenece a un término de taxonomía

Comprobar si una entrada pertenece a un término de taxonomía

Última verificación: 12 de octubre de 2026
17 min de lectura
Tutorial
Desarrollador full-stack

Un requerimiento habitual al programar temas y plugins en WordPress consiste en mostrar un elemento de la interfaz, ejecutar reglas de negocio o cargar scripts condicionales únicamente cuando una entrada pertenece a un término específico de una taxonomía. Los desarrolladores se encuentran a menudo con comportamientos inesperados al configurar banners promocionales, distintivos de producto o tablas de precios porque WordPress ofrece diversas herramientas de comprobación con distintos ámbitos y modelos de almacenamiento en caché.

Dos funciones principales se confunden con frecuencia: is_tax() y has_term(). Aunque sus nombres guarden similitud, resuelven problemas arquitectónicos totalmente distintos. La función is_tax() evalúa el contexto global de la consulta en una página de archivo, mientras que has_term() comprueba las asignaciones directas de un registro de entrada concreto. Además, el núcleo de WordPress dispone de utilidades de menor nivel como is_object_in_term(), wp_get_post_terms(), get_the_terms() y envolturas especializadas como in_category().

Comprender el rendimiento de cada opción resulta vital para prevenir el problema de consultas N+1 en archivos densos y arquitecturas desacopladas o headless. Cuando se verifican taxonomías en bucles sobre cientos de entradas mediante funciones inadecuadas, se generan decenas de consultas SQL innecesarias hacia la base de datos.


#Diferencias entre contexto de archivo global y asignación de entrada

Antes de escribir condiciones lógicas en las plantillas, es imprescindible distinguir la inspección del contexto de la petición respecto a la comprobación de relaciones a nivel de registro. Mezclar ambos conceptos es la causa principal de fallos en plantillas de WordPress.

#El contexto de archivo con is_tax

La función is_tax() es una etiqueta condicional diseñada de forma exclusiva para el enrutamiento de plantillas de archivo. Comprueba el objeto principal de la consulta a través de $wp_query->is_tax() para saber si el usuario se encuentra navegando en el listado de una taxonomía personalizada.

// Activo exclusivamente en archivos de taxonomía, por ejemplo /genero/ciencia-ficcion/
if ( is_tax( 'genero', 'ciencia-ficcion' ) ) {
    echo '<aside class="archive-callout">Estás explorando el catálogo de Ciencia Ficción.</aside>';
}

Si el usuario visita una entrada individual como /peliculas/interstellar/, is_tax() devolverá siempre false, incluso si dicha película pertenece al género de ciencia ficción. La función examina los parámetros de la solicitud HTTP y el archivo actual, no los datos del contenido que se está renderizando.

#La asignación de la entrada con has_term

Para determinar si una entrada individual está asociada a un término de taxonomía, se debe utilizar has_term(). Esta función inspecciona las relaciones guardadas entre el ID de la entrada y los términos en la base de datos:

// Activo en entradas individuales o bucles donde la entrada posee la asignación
if ( has_term( 'ciencia-ficcion', 'genero' ) ) {
    echo '<span class="badge genre-badge">Título destacado de Ciencia Ficción</span>';
}

Cuando se invoca sin indicar un objeto de entrada o ID, has_term() recurre de forma predeterminada al ID del post global mediante get_the_ID(). Fuera del bucle principal, como en shortcodes o filtros, siempre conviene especificar el ID de forma explícita.


#Funciones principales de WordPress para inspeccionar términos

El núcleo de WordPress proporciona una API de taxonomías diseñada para cubrir distintas capas del sistema, desde comprobaciones rápidas en plantillas hasta la validación de relaciones directas en la base de datos.

#Uso práctico de has_term

La signatura de has_term() acepta tres parámetros: el identificador del término, el nombre de la taxonomía y un objeto o ID de entrada opcional:

has_term( string|int|array $term = '', string $taxonomy = '', int|WP_Post $post = null ): bool

El parámetro $term admite slugs en texto, nombres legibles, identificadores numéricos o una matriz con varios de ellos. Si se pasa una matriz, la función valida si la entrada contiene al menos uno de los términos indicados:

// Comprobación simultánea de varios géneros admitidos
$generos_monitoreados = [ 'terror', 'thriller', 'misterio' ];

if ( has_term( $generos_monitoreados, 'genero', $post_id ) ) {
    get_template_part( 'partials/content', 'dark-theme' );
}

#Propósito e implementación de in_category

La función in_category() es una envoltura cómoda construida alrededor de has_term(), reservada en exclusiva para la taxonomía predeterminada category:

if ( in_category( 'noticias-destacadas', $post_id ) ) {
    echo '<div class="alert-banner">Aviso editorial importante</div>';
}

Internamente, in_category() traslada sus argumentos de manera directa a has_term( $category, 'category', $post ). En categorías estándar de blog resulta muy legible, pero cuando se trabaja con taxonomías personalizadas es obligatorio recurrir a has_term().

#Comprobación directa de relaciones con is_object_in_term

En la base técnica de has_term() interviene la función de bajo nivel is_object_in_term(). Situada en wp-includes/taxonomy.php, gestiona la consulta directa de relaciones entre un identificador de objeto y un conjunto de términos:

is_object_in_term( int $object_id, string $taxonomy, int|string|array $terms = null ): bool|WP_Error

A diferencia de has_term(), que obtiene la entrada del bucle global por defecto y siempre devuelve un valor booleano, is_object_in_term() requiere un ID de objeto explícito y puede devolver un WP_Error si la taxonomía no es válida. Se utiliza en migraciones de datos, repositorios abstractos y procesos de administración por lotes.

#Obtención de términos en memoria mediante get_the_terms

Cuando una plantilla necesita acceder a metadatos adicionales de los términos, como nombres, metacampos o enlaces permanentes, además de la comprobación condicional, la herramienta adecuada es get_the_terms():

$terminos_asignados = get_the_terms( $post_id, 'genero' );

if ( ! empty( $terminos_asignados ) && ! is_wp_error( $terminos_asignados ) ) {
    foreach ( $terminos_asignados as $objeto_termino ) {
        if ( $objeto_termino->slug === 'cyberpunk' ) {
            // Renderizado específico para entradas de estilo cyberpunk
            break;
        }
    }
}

La ventaja de get_the_terms() reside en su integración con la memoria caché de objetos de WordPress. Utiliza el grupo de caché post_tag_relationships, permitiendo que llamadas posteriores dentro de la misma petición recuperen la información directamente desde la memoria RAM del servidor.


#Comprobaciones jerárquicas y validación de términos padre e hijos

Las taxonomías de WordPress se dividen en estructuras planas, como las etiquetas, y jerárquicas, como las categorías. En las jerárquicas existe una particularidad técnica esencial: asociar una entrada a un término hijo no implica que quede vinculada automáticamente a los términos ascendientes en la base de datos.

Imaginemos la siguiente jerarquía:

  • Entretenimiento (ID: 100)
    • Videojuegos (ID: 101)
      • Juegos de rol RPG (ID: 102)

Si un redactor marca únicamente “Juegos de rol RPG”, ejecutar has_term( 100, 'category', $post_id ) devolverá false. En la tabla wp_term_relationships, la entrada solo cuenta con un registro para el término 102.

#Comprobación de ascendencia con term_is_ancestor_of

WordPress dispone de la función term_is_ancestor_of(), pensada para determinar si un término padre es antecesor de otro término hijo. Combinando get_the_terms() con term_is_ancestor_of() logramos un validador eficiente:

/**
 * Comprueba si una entrada pertenece a un término padre o a cualquiera de sus descendientes.
 *
 * @param int|string $target_term Término objetivo (ID numérico o slug).
 * @param string     $taxonomy    Nombre de la taxonomía.
 * @param int|null   $post_id     ID de la entrada. Por defecto, la entrada actual.
 * @return bool Verdadero si coincide directamente o mediante relación de ascendencia.
 */
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;
    }

    // Resolver identificador numérico fiable
    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;
    }

    // Comprobación directa: ruta más rápida
    if ( has_term( $parent_term_id, $taxonomy, $post_id ) ) {
        return true;
    }

    // Obtener términos de la entrada desde la caché de objetos
    $post_terms = get_the_terms( $post_id, $taxonomy );
    if ( empty( $post_terms ) || is_wp_error( $post_terms ) ) {
        return false;
    }

    // Evaluar si alguno de los términos asignados es descendiente del padre
    foreach ( $post_terms as $assigned_term ) {
        if ( term_is_ancestor_of( $parent_term_id, $assigned_term->term_id, $taxonomy ) ) {
            return true;
        }
    }

    return false;
}

#Resolución de descendientes con get_term_children

Otra estrategia consiste en obtener todos los IDs de los términos descendientes por anticipado con get_term_children() y pasar la matriz resultante a has_term():

/**
 * Comprueba asignaciones en una rama resolviendo los IDs descendientes.
 *
 * @param int    $parent_term_id ID numérico del padre.
 * @param string $taxonomy       Nombre de la taxonomía.
 * @param int    $post_id        ID de la entrada.
 * @return bool
 */
function wppoland_has_term_branch_check( int $parent_term_id, string $taxonomy, int $post_id ): bool {
    // 1. Comprobación directa
    if ( has_term( $parent_term_id, $taxonomy, $post_id ) ) {
        return true;
    }

    // 2. Obtener todos los descendientes de la rama
    $descendant_ids = get_term_children( $parent_term_id, $taxonomy );

    if ( empty( $descendant_ids ) || is_wp_error( $descendant_ids ) ) {
        return false;
    }

    // 3. has_term admite colecciones de IDs numéricos
    return has_term( $descendant_ids, $taxonomy, $post_id );
}

En estructuras con poca profundidad, la alternativa basada en term_is_ancestor_of() consume menos memoria porque solo opera sobre la pequeña lista de términos asignados a la entrada. Si la taxonomía cuenta con miles de categorías anidadas, calcular todos los hijos con get_term_children() crea matrices pesadas en memoria, haciendo que la verificación sobre los términos del post resulte sensiblemente más ágil.


#Rendimiento en bucles y estrategias de memoria caché de objetos

Ejecutar comprobaciones de términos en bucles que muestran decenas de publicaciones puede provocar graves cuellos de botella en la base de datos si las funciones no se eligen adecuadamente. En tiendas WooCommerce o catálogos corporativos, esto desemboca con rapidez en el problema de consultas N+1.

#El problema N+1 en bucles de taxonomía

Si un bucle recorre 50 entradas e invoca en cada paso has_term( 'premium', 'nivel_membresia', $item->ID ) mediante un slug de texto en lugar de un ID, y los términos no se precargaron en memoria, cada iteración genera consultas SQL individuales hacia las tablas wp_term_relationships y wp_term_taxonomy.

Escenario típico de 50 entradas sin precarga:

  • 1 consulta SQL para obtener las entradas.
  • 50 consultas SQL para resolver los términos de cada entrada.
  • 50 consultas SQL para traducir slugs a IDs en caso de caché fría.
  • Total: 101 consultas a la base de datos para un único componente visual.

#Precarga de caché con update_post_term_cache

El núcleo de WordPress incorpora un mecanismo preventivo. Al instanciar WP_Query, el argumento update_post_term_cache viene habilitado por defecto con valor true:

$catalogo_query = new WP_Query( [
    'post_type'              => 'curso',
    'posts_per_page'         => 30,
    'update_post_term_cache' => true, // Precarga todos los términos de los posts en 1 sola consulta SQL
    'update_post_meta_cache' => true,
    'no_found_rows'          => true, // Desactiva SQL_CALC_FOUND_ROWS si no hay paginación
] );

Con update_post_term_cache activo, WordPress recopila todos los IDs de entrada y ejecuta una única consulta por lotes:

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;

Los objetos obtenidos se almacenan inmediatamente en la caché de objetos (wp_cache_set). Cualquier llamada posterior a has_term(), get_the_terms() o wp_get_post_terms() se resuelve directamente en memoria sin consultas SQL adicionales.

#Mapas en memoria para procesamiento masivo

En procesos en segundo plano, comandos de WP-CLI o exportaciones masivas en la API REST, llamar reiteradamente a has_term() produce sobrecarga de ejecución en PHP. En tales escenarios, conviene utilizar un filtrado basado en mapas en memoria:

/**
 * Filtra una lista amplia de entradas por término usando extracción masiva de caché.
 *
 * @param int[]  $post_ids Identificadores de las entradas.
 * @param string $taxonomy Nombre de la taxonomía.
 * @param int    $term_id  ID numérico del término a contrastar.
 * @return int[] Identificadores filtrados que poseen el término.
 */
function wppoland_bulk_filter_posts_by_term( array $post_ids, string $taxonomy, int $term_id ): array {
    if ( empty( $post_ids ) ) {
        return [];
    }

    // Precargar todos los términos en memoria con una sola llamada SQL
    update_object_term_cache( $post_ids, 'post' );

    $coincidencias = [];

    foreach ( $post_ids as $id ) {
        // Lectura directa desde la memoria caché de objetos
        $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 ) {
                $coincidencias[] = $id;
                break;
            }
        }
    }

    return $coincidencias;
}

#Comparativa de rendimiento entre has_term y caché de términos precargada

Para medir el impacto de las diferentes técnicas de comprobación de términos, realizamos un banco de pruebas en WordPress 6.8 con PHP 8.3 y Redis Object Cache activado.

El conjunto de prueba integró 250 entradas, cada una con un promedio de 4 términos asignados en una taxonomía personalizada. Evaluamos tres patrones comunes:

  1. Patrón A: Empleo de has_term() con slugs en texto sin precarga de caché.
  2. Patrón B: Empleo de has_term() con IDs numéricos y precarga habitual de consulta.
  3. Patrón C: Empleo de get_the_terms() combinado con una comprobación en memoria mediante matrices asociativas de PHP (isset()).

#Mediciones de rendimiento en 250 entradas

La siguiente tabla detalla nuestras mediciones registradas durante las pruebas sintéticas:

Patrón arquitectónicoTiempo total (250 entradas)Consultas SQL ejecutadasPico de memoria RAMAcierto de caché
Patrón A: has_term( slug ) sin caché48.72 ms251 consultas3.42 MB12.4%
Patrón B: has_term( ID ) con caché3.14 ms1 consulta3.58 MB99.6%
Patrón C: get_the_terms + hash set1.82 ms1 consulta3.61 MB100.0%

#Análisis de los resultados

Las mediciones reflejan tres conclusiones técnicas claras:

Primero, recurrir a slugs en has_term() cuando la caché está vacía resulta muy costoso. WordPress debe resolver cada slug mediante get_term_by(), lo que multiplica los tiempos de carga por casi quince veces en escenarios sin caché caliente.

Segundo, mantener activo update_post_term_cache reduce las consultas SQL de 251 a una sola petición por lotes. Los tiempos bajan a 3.14 milisegundos para 250 elementos, garantizando un renderizado ágil en plantillas web.

Tercero, en endpoints de alto tráfico o sincronizaciones de datos masivas, recuperar la colección completa con get_the_terms() y verificar claves de matriz en PHP ofrece una mejora adicional del 40% frente a invocaciones sucesivas de has_term().


#Filtrado en WP_Query y consultas directas a la base de datos

La comprobación de términos también puede delegarse al motor de base de datos antes de transferir datos hacia PHP. Descartar registros no deseados mediante SQL preserva memoria en el servidor.

#Uso del parámetro tax_query en WP_Query

La forma estándar de restringir resultados según términos de taxonomía es el argumento tax_query en 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, // Inclusión automática de términos hijos
        ],
        [
            'taxonomy' => 'product_visibility',
            'field'    => 'slug',
            'terms'    => 'exclude-from-catalog',
            'operator' => 'NOT IN',
        ],
    ],
] );

El valor include_children es de gran utilidad. Cuando se fija en true (el valor predeterminado para taxonomías jerárquicas), WordPress resuelve los descendientes con get_term_children() e incorpora todos los IDs a la cláusula SQL WHERE ... IN (...), evitando recorrer el árbol de términos manualmente en PHP.

#Consultas SQL directas con wpdb

En paneles de administración, informes o tareas de mantenimiento masivo puede resultar conveniente comprobar relaciones sin cargar modelos WP_Post completos. Para ello se puede utilizar el objeto global $wpdb:

global $wpdb;

$id_entrada_comprobar = 1420;
$id_termino_objetivo  = 88;
$nombre_taxonomia     = 'tipo_proyecto';

$esta_asignado = (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_entrada_comprobar,
    $nombre_taxonomia,
    $id_termino_objetivo
) );

if ( $esta_asignado ) {
    // Continuar procesamiento ligero
}

Las consultas directas con $wpdb omiten la caché de WordPress. Solo deben reservarse para tareas por lotes en terminal o migraciones donde la caché de objetos podría saturar la memoria.


#Verificación de taxonomías en la API REST y arquitecturas headless

En aplicaciones modernas donde WordPress actúa como backend para sitios desarrollados con Next.js, Astro o plataformas móviles, las comprobaciones de taxonomía requieren serializaciones limpias en los endpoints.

#Exposición de taxonomías en respuestas de la API REST

Al declarar una taxonomía con register_taxonomy(), configurar el parámetro 'show_in_rest' => true añade automáticamente la lista de IDs asignados en las respuestas JSON:

register_taxonomy( 'sector_estudio', 'case_study', [
    'labels'            => [ 'name' => 'Sectores' ],
    'hierarchical'      => true,
    'show_in_rest'      => true,
    'rest_base'         => 'case-study-sectores',
    'rest_controller_class' => 'WP_REST_Terms_Controller',
] );

La respuesta del endpoint /wp/v2/case_study/580 entregará una matriz con los IDs asignados:

{
  "id": 580,
  "date": "2026-10-12T10:00:00",
  "slug": "modernizacion-infraestructura-cloud",
  "case-study-sectores": [ 14, 29 ]
}

El frontend puede verificar la pertenencia con métodos nativos de JavaScript, como post['case-study-sectores'].includes(14).

#Campos calculados en controladores REST personalizados

Al crear rutas personalizadas mediante register_rest_route(), se pueden registrar campos calculados que devuelvan valores booleanos ya resueltos:

add_action( 'rest_api_init', function() {
    register_rest_field( 'case_study', 'es_cliente_enterprise', [
        'get_callback' => function( array $post_arr ) {
            $post_id = $post_arr['id'];
            
            // Comprobación de asignación a Enterprise o sus términos hijos
            return wppoland_post_has_term_or_descendant( 'enterprise', 'segmento_cliente', $post_id );
        },
        'schema' => [
            'description' => 'Determina si el caso de estudio pertenece a un cliente enterprise.',
            'type'        => 'boolean',
            'context'     => [ 'view', 'embed' ],
        ],
    ] );
} );

Este esquema traslada la resolución jerárquica al servidor, entregando una propiedad booleana limpia al consumidor de la API.


#Errores habituales y pautas de programación defensiva

Al trabajar con condiciones de taxonomía suelen cometerse pequeños errores que desembocan en problemas difíciles de depurar en producción.

#Error uno: Uso de nombres en texto en proyectos multilingües

Verificar nombres visibles mediante has_term( 'Noticias y Novedades', 'category' ) falla con facilidad si existen entidades HTML o traducciones en plugins multilingües (WPML, Polylang). El slug o nombre cambia según el idioma, rompiendo la lógica. Es preferible apoyarse siempre en identificadores numéricos constantes.

#Error dos: Llamar a has_term fuera del bucle sin ID de entrada

Invocar has_term( 'destacado', 'etiqueta_estado' ) en barras laterales o pies de página sin pasar $post->ID puede evaluar una entrada errónea si una consulta secundaria previa no ejecutó wp_reset_postdata().

Pauta defensiva:

// Suministrar siempre el ID explícito en componentes modulares
$id_actual = get_the_ID();
if ( $id_actual && has_term( 22, 'etiqueta_estado', $id_actual ) ) {
    get_template_part( 'template-parts/badges/ribbon' );
}

#Error tres: Confundir funciones de etiquetas con taxonomías personalizadas

Funciones como has_tag() solo operan sobre la taxonomía estándar post_tag. Si se intenta comprobar una taxonomía personalizada con ella, la función no arrojará resultados correctos. Se recomienda estandarizar el código utilizando siempre has_term().


#Resumen técnico y lista de comprobación recomendada

Comprobar correctamente los términos de taxonomía es esencial para mantener temas y plugins ágiles y robustos. Sigue esta lista de verificación en tus proyectos:

  1. Distinguir el alcance: Usa is_tax() únicamente para páginas de archivo y has_term() para registros de entrada individuales.
  2. Priorizar IDs numéricos: Evita dependencias de slugs o nombres traducibles para asegurar estabilidad y velocidad.
  3. Gestionar la jerarquía de forma deliberada: Recuerda que has_term() no hereda términos; usa term_is_ancestor_of() o get_term_children() si necesitas comprobar ramas completas.
  4. Optimizar la memoria caché: Mantén update_post_term_cache activo en WP_Query para erradicar el problema N+1.
  5. Restaurar el contexto de la consulta: Invoca wp_reset_postdata() tras bucles secundarios para no alterar las comprobaciones del post global.

Si tu equipo gestiona aplicaciones críticas de WordPress, tiendas WooCommerce de gran escala o sistemas headless y necesita auditorías de código o desarrollo especializado, consulta nuestros servicios de desarrollo en WordPress.

Siguiente paso

Transforma el artículo en una implementación real

Este bloque refuerza el enlazado interno y lleva al lector al siguiente paso más útil dentro de la arquitectura del sitio.

¿Quieres implementar esto en tu sitio?

Si quieres transformar el artículo en mejoras concretas, rediseño o un plan de implementación, puedo cerrar el alcance y ejecutar.

Cluster relacionado

Explora otros servicios WordPress y base de conocimiento

Refuerza tu negocio con soporte técnico profesional en áreas clave del ecosistema WordPress.

Preguntas frecuentes

Respuestas prácticas para aplicar el tema en la ejecución real.

¿Cuál es la diferencia entre has_term() e is_tax()?#
has_term() comprueba si una entrada concreta está asignada a un término, mientras que is_tax() evalúa el contexto de una página de archivo. Son herramientas distintas para propósitos diferentes.
¿Cómo incluyo términos hijos en la comprobación?#
Debes emplear una función auxiliar que examine la jerarquía mediante term_is_ancestor_of() o recupere los descendientes con get_term_children().
¿Es mejor usar IDs o slugs en condiciones de taxonomía?#
Los IDs numéricos son más eficientes y fiables, especialmente en sitios multilingües donde los slugs pueden variar según el idioma.

¿Necesitas un FAQ adaptado a tu sector y mercado? Preparamos una versión alineada con tus objetivos de negocio.

Hablemos

Artículos Relacionados