Introduccion a la recuperación de posts basada en categorías
Conoce más sobre el desarrollo WordPress profesional en WPPoland.
Una de las tareas más comunes en el desarrollo de WordPress es recuperar posts de categorías específicas. Ya sea que estes construyendo un diseño de homepage personalizado, creando una plantilla de archivo de categoría o mostrando contenido relacionado, entender como consultar posts por categoría de forma eficiente es esencial para cualquier desarrollador WordPress.
Esta guía completa cubre múltiples enfoques para extraer listas de posts de categorías, desde implementaciones simples hasta técnicas avanzadas de optimización. Al final, tendras un kit de herramientas completo para manejar consultas basadas en categorías en cualquier proyecto WordPress.
Entendiendo las categorías de WordPress
Antes de profundizar en el código, es importante entender como WordPress maneja las categorías:
- Las categorías son una taxonomía incorporada en WordPress
- Cada post puede pertenecer a múltiples categorías
- Las categorías pueden ser jerárquicas (relaciones padre/hijo)
- Los datos de categoría se almacenan en las tablas
wp_termsywp_term_taxonomy - Las relaciones post-categoría se almacenan en
wp_term_relationships
Entender esta estructura te ayuda a escribir consultas más eficientes y soluciónar problemas cuando surjan.
Encontrar el ID de una categoría a partir de su slug o nombre
La mayoría de los argumentos de consulta aceptan tanto un slug como un ID numérico, pero los argumentos basados en ID (cat, category__in) son los más rápidos porque se saltan la búsqueda del término. Cuando solo conoces el slug, resuélvelo una vez y reutiliza el ID:
// From a slug
$term = get_category_by_slug('news');
$news_id = $term ? $term->term_id : 0;
// From a display name
$news_id = get_cat_ID('News'); // returns 0 if not found
// From any taxonomy (categories included)
$term = get_term_by('slug', 'news', 'category');
Cachea el ID resuelto en una constante o en una opción si lo dejas fijo en una plantilla; llamar a get_cat_ID() en cada petición añade una consulta que puedes evitar. En proyectos que hemos migrado desde alojamientos como SiteGround o Raiola Networks, este pequeño detalle es de los que más se repiten en temas heredados: decenas de get_cat_ID() dispersos por el functions.php.
Método 1: WP_Query (El enfoque flexible)
WP_Query es la clase principal de WordPress para consultar posts. Ofrece máxima flexibilidad y es el enfoque recomendado para la mayoría de los casos de uso.
Consulta básica por categoría
$args = array(
'category_name' => 'noticias',
'posts_per_page' => 10,
'orderby' => 'date',
'order' => 'DESC'
);
$query = new WP_Query($args);
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
// Mostrar contenido del post
?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<div class="entry-content">
<?php the_excerpt(); ?>
</div>
</article>
<?php
}
wp_reset_postdata();
}
Consulta por ID de categoría
$args = array(
'cat' => 5, // ID de categoria
'posts_per_page' => 5
);
$query = new WP_Query($args);
Multiples categorías
// Posts en CUALQUIERA de estas categorias (relacion OR)
$args = array(
'category__in' => array(5, 10, 15),
'posts_per_page' => 10
);
// Posts en TODAS estas categorias (relacion AND)
$args = array(
'category__and' => array(5, 10),
'posts_per_page' => 10
);
// Excluir categorias específicas
$args = array(
'category__not_in' => array(3, 7),
'posts_per_page' => 10
);
Incluyendo categorías hijas
// Obtener posts de la categoria y todas sus hijas
$id_categoria_padre = 5;
$args = array(
'cat' => $id_categoria_padre,
'posts_per_page' => 20
);
// WP_Query incluye automáticamente categorias hijas cuando usas 'cat'
Método 2: get_posts() (El enfoque simple)
Para casos de uso más simples, get_posts() proporciona una API más directa.
Uso básico
$posts = get_posts(array(
'category' => 5,
'posts_per_page' => 10,
'orderby' => 'date',
'order' => 'DESC'
));
foreach ($posts as $post) {
setup_postdata($post);
?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
</article>
<?php
}
wp_reset_postdata();
Con nombre de categoría
$posts = get_posts(array(
'category_name' => 'tecnología',
'numberposts' => 5
));
Método 3: Shortcodes para editores de contenido
Crear un shortcode permite a los editores de contenido insertar listas de posts por categoría en cualquier lugar.
function shortcode_posts_categoria($atts) {
$atts = shortcode_atts(array(
'categoria' => '',
'posts' => 5,
'orderby' => 'date',
'order' => 'DESC'
), $atts);
$args = array(
'category_name' => $atts['categoria'],
'posts_per_page' => intval($atts['posts']),
'orderby' => $atts['orderby'],
'order' => $atts['order']
);
$query = new WP_Query($args);
ob_start();
if ($query->have_posts()) {
echo '<div class="lista-posts-categoria">';
while ($query->have_posts()) {
$query->the_post();
?>
<article class="post-categoria">
<h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
<p><?php the_excerpt(); ?></p>
</article>
<?php
}
echo '</div>';
} else {
echo '<p>No se encontraron posts en esta categoria.</p>';
}
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode('posts_categoria', 'shortcode_posts_categoria');
Uso: [posts_categoria categoría="noticias" posts="5"]
Método 4: Modificar la consulta principal
Cuando quieras cambiar que posts aparecen en las páginas de archivo de categoría, usa la acción pre_get_posts.
function modificar_consultas_categoria($query) {
// Solo modificar archivos de categoria en la consulta principal
if ($query->is_category() && $query->is_main_query() && !is_admin()) {
// Mostrar 20 posts por página en lugar del predeterminado
$query->set('posts_per_page', 20);
// Excluir posts de categoria específica en ciertas páginas de categoria
$cat_actual = get_queried_object();
if ($cat_actual->slug === 'destacados') {
$query->set('category__not_in', array(10)); // Excluir categoria ID 10
}
}
}
add_action('pre_get_posts', 'modificar_consultas_categoria');
Método 5: tax_query para un filtrado preciso y consciente de la taxonomía
Los atajos cat y category_name son cómodos, pero tax_query es el argumento al que recurres cuando los requisitos se vuelven específicos: combinar categorías con etiquetas, excluir términos hijos o consultar una taxonomía personalizada. Además expresa la lógica AND/OR de forma explícita, algo que los argumentos abreviados no pueden hacer.
$args = array(
'posts_per_page' => 10,
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array('news'),
),
array(
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => array('featured'),
),
),
);
$query = new WP_Query($args);
La clave include_children es el detalle práctico que la mayoría pasa por alto. Por defecto, una tax_query sobre category arrastra todos los términos descendientes, lo que suele ser lo que quiere un archivo pero rara vez lo que quiere un bloque curado de la homepage:
'tax_query' => array(
array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => array(5),
'include_children' => false, // only posts filed directly under term 5
),
),
Como tax_query funciona contra cualquier taxonomía, el mismo patrón recupera posts de un product_cat de WooCommerce, un portfolio_type o cualquier taxonomía que registre tu tema, sin necesidad de una función distinta por taxonomía. En tiendas WooCommerce en español es habitual reutilizar este patrón para listar productos de una categoría concreta sin duplicar código.
Optimización de rendimiento
1. Usar Transients para consultas costosas
function obtener_posts_categoria_con_cache($id_categoria, $cantidad = 5) {
$clave_cache = 'cat_posts_' . $id_categoria . '_' . $cantidad;
$posts = get_transient($clave_cache);
if (false === $posts) {
$args = array(
'cat' => $id_categoria,
'posts_per_page' => $cantidad
);
$query = new WP_Query($args);
$posts = $query->posts;
// Cache por 1 hora
set_transient($clave_cache, $posts, HOUR_IN_SECONDS);
}
return $posts;
}
2. Optimizar consultas de base de datos
// Solo recuperar los campos que necesitas
$args = array(
'category_name' => 'noticias',
'posts_per_page' => 10,
'fields' => 'ids' // Solo obtener IDs de posts para mejor rendimiento
);
$query = new WP_Query($args);
3. Usar Object Caching
Si tu sitio usa un object cache (Redis, Memcached), los resultados de WP_Query se cachean automáticamente, mejorando el rendimiento para consultas repetidas.
Técnicas avanzadas
Plantillas personalizadas para archivos de categoría
Crea un archivo de plantilla category-noticias.php para estilos específicos de categoría:
<?php
/* Template Name: Categoria - Noticias */
get_header(); ?>
<div class="archivo-categoria">
<h1><?php single_cat_title(); ?></h1>
<?php if (have_posts()) : ?>
<div class="cuadricula-posts">
<?php while (have_posts()) : the_post(); ?>
<?php get_template_part('content', 'category'); ?>
<?php endwhile; ?>
</div>
<?php the_posts_págination(); ?>
<?php else : ?>
<p>No se encontraron posts en esta categoria.</p>
<?php endif; ?>
</div>
<?php get_footer(); ?>
Carga AJAX para posts de categoría
Para una mejor experiencia de usuario, implementa carga AJAX. Hay dos detalles que hacen tropezar a mucha gente: ajaxurl solo está definido en wp-admin, así que en el front end tienes que pasarlo tú mismo, y cada petición necesita un nonce para superar una revisión de seguridad.
Encola el script y entrégale la URL de admin-ajax junto con un nonce:
function enqueue_category_loader() {
wp_enqueue_script(
'category-loader',
get_theme_file_uri('/js/category-loader.js'),
array('jquery'),
'1.0',
true
);
wp_localize_script('category-loader', 'catLoader', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('load_category_posts'),
));
}
add_action('wp_enqueue_scripts', 'enqueue_category_loader');
La petición desde el front end lee entonces del objeto localizado:
jQuery(document).ready(function($) {
$('.load-more').on('click', function() {
var button = $(this);
$.ajax({
url: catLoader.ajaxurl,
type: 'POST',
data: {
action: 'load_category_posts',
nonce: catLoader.nonce,
category: button.data('category'),
page: button.data('page')
},
success: function(response) {
$('.posts-container').append(response);
button.data('page', button.data('page') + 1);
}
});
});
});
Por último, registra el handler tanto para usuarios autenticados como anónimos (wp_ajax_ y wp_ajax_nopriv_), verifica el nonce y devuelve el marcado renderizado:
function load_category_posts_handler() {
check_ajax_referer('load_category_posts', 'nonce');
$category = sanitize_text_field($_POST['category'] ?? '');
$page = max(1, intval($_POST['page'] ?? 1));
$query = new WP_Query(array(
'category_name' => $category,
'posts_per_page' => 5,
'paged' => $page,
));
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
printf(
'<article><h3><a href="%s">%s</a></h3></article>',
esc_url(get_permalink()),
esc_html(get_the_title())
);
}
wp_reset_postdata();
}
wp_die(); // required so admin-ajax stops cleanly
}
add_action('wp_ajax_load_category_posts', 'load_category_posts_handler');
add_action('wp_ajax_nopriv_load_category_posts', 'load_category_posts_handler');
Mostrar posts de categoría en un tema de bloques
En los temas de bloques (el estándar desde WordPress 6.1) a menudo no necesitas PHP en absoluto. El bloque Query Loop filtrado por categoría muestra la misma lista desde el editor: inserta un Query Loop, abre los ajustes del bloque y, en Filtros, añade la categoría que quieras. Guárdalo como un patrón sincronizado y los editores podrán colocar la lista curada en cualquier página sin tocar una plantilla.
Recurre a los enfoques con código de arriba cuando necesites lógica que el bloque no puede expresar: cache con transients, exclusiones condicionales por archivo o una salida que consuma algo distinto del tema (un boletín por correo, una respuesta REST).
Obtener posts de categoría a través de la REST API
Para front ends headless, una isla de React o una integración externa, WordPress expone los posts de categoría a través de la REST API sin necesidad de un endpoint personalizado:
GET /wp-json/wp/v2/posts?categories=5&per_page=10&_fields=id,title,link,excerpt
Usa categories_exclude para filtrar un término, _embed para traer las imágenes destacadas y los nombres de términos en una sola llamada, y _fields para reducir la carga a lo que el cliente realmente muestra. Para resolver un slug al ID que espera el endpoint, consulta primero /wp-json/wp/v2/categories?slug=news.
Depurar una consulta de categoría que devuelve los posts equivocados
Cuando una consulta devuelve demasiados posts, muy pocos o posts inesperados, inspecciona el SQL que realmente ejecutó en lugar de adivinar con los argumentos. Instala Query Monitor y te mostrará todas las consultas de la página, incluida la que genera tu loop, con las lentas marcadas. Para una comprobación rápida sin plugin, vuelca la consulta ya interpretada:
$query = new WP_Query($args);
// The exact SQL WordPress built from your $args
error_log($query->request);
Los dos culpables detrás de la mayoría de las sorpresas son la inclusión de términos hijos (mira include_children más arriba) y algún filtro pre_get_posts perdido de un plugin o del tema que altera la consulta principal. Si el SQL parece correcto pero el resultado está vacío, confirma que el término tenga realmente posts publicados asignados en el idioma o contexto actual. En sitios multilingües con Polylang o WPML, esto último es la causa más frecuente en el mercado español: la categoría existe, pero los posts están asignados a la versión en otro idioma.
Errores comunes a evitar
- No restablecer post data: Siempre llama a
wp_reset_postdata()después de loops personalizados - Consultar en cada carga de página: Usa cache para consultas costosas
- No verificar si existen posts: Siempre verifica
have_posts()antes de iterar - Modificar la consulta principal incorrectamente: Usa
pre_get_postsen lugar de crear nuevas consultas en páginas de archivo - Ignorar la páginación: Recuerda manejar la páginación para archivos de categoría grandes
Conclusion
WordPress proporciona múltiples formás de extraer posts de categorías, cada una adecuada para diferentes escenarios:
- WP_Query: Mejor para visualizaciones complejas y personalizadas
- get_posts(): Ideal para listas simples de posts
- Shortcodes: Perfecto para flexibilidad del editor de contenido
- pre_get_posts: Esencial para modificar páginas de archivo
Entender estos métodos y cuando usar cada uno te hara un desarrollador WordPress más efectivo. Recuerda siempre considerar el rendimiento, especialmente en sitios con grandes cantidades de contenido.
Para sitios en producción, implementa estrategias de cache y prueba tus consultas con herramientas como Query Monitor para asegurar un rendimiento óptimo.
Necesitas ayuda con consultas avanzadas de WordPress? Nuestro equipo de desarrollo WordPress puede implementar soluciones optimizadas para tu proyecto. Contactaños.







