WP_Query vs get_posts(): cuándo usar cada uno
Una de las preguntas más frecuentes cuando empiezas a desarrollar en WordPress es cuándo usar WP_Query y cuándo get_posts(). Ambas sirven para obtener posts de la base de datos, pero tienen diferencias importantes que afectan al rendimiento y al comportamiento de tu código.
En este artículo te explico las diferencias reales con ejemplos concretos.
WP_Query — la clase completa
WP_Query es la clase principal de WordPress para consultas a la base de datos. Es la más potente y la más flexible de las dos opciones.
$args = array(
'post_type' => 'producto',
'posts_per_page' => 6,
'orderby' => 'date',
'order' => 'DESC',
'tax_query' => array(
array(
'taxonomy' => 'categoria_producto',
'field' => 'slug',
'terms' => 'novedades',
),
),
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<?php the_excerpt(); ?>
</article>
<?php
}
wp_reset_postdata(); // SIEMPRE después del loop
}
La línea wp_reset_postdata() al final es crítica. Cuando usas the_post() dentro del loop, WordPress modifica el post global $post. Si no reseteas, otras partes de la página pueden mostrar datos incorrectos.
get_posts() — la función simplificada
get_posts() es internamente un wrapper de WP_Query con algunos parámetros preconfigurados. Devuelve directamente un array de objetos WP_Post, sin necesidad de un loop:
$productos = get_posts( array(
'post_type' => 'producto',
'posts_per_page' => 6,
'orderby' => 'title',
'order' => 'ASC',
) );
if ( ! empty( $productos ) ) {
foreach ( $productos as $producto ) {
echo '<h2>' . esc_html( $producto->post_title ) . '</h2>';
}
}
Más limpio para casos simples. Sin loop, sin wp_reset_postdata().
Las diferencias clave
Estas son las diferencias que realmente importan en el día a día:
1. suppress_filters
Por defecto, get_posts() tiene suppress_filters = true, lo que significa que ignora los filtros registrados sobre la query, incluyendo los de plugins como WPML o Polylang. Si necesitas que los filtros funcionen, debes desactivarlo explícitamente:
$posts = get_posts( array(
'post_type' => 'producto',
'suppress_filters' => false, // Activar filtros
) );
2. Paginación
WP_Query soporta paginación nativa con paged y paginate_links(). get_posts() no. Si necesitas paginar, usa siempre WP_Query:
$paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;
$query = new WP_Query( array(
'post_type' => 'producto',
'posts_per_page' => 6,
'paged' => $paged,
) );
// Al final del loop:
echo paginate_links( array(
'total' => $query->max_num_pages,
'current' => $paged,
) );
wp_reset_postdata();
3. Funciones de template
Dentro de un loop de WP_Query (con the_post()), puedes usar todas las funciones de template de WordPress: the_title(), the_content(), the_permalink(), the_thumbnail(). Con get_posts() trabajas directamente con el objeto $post y sus propiedades.
Cuándo usar cada uno — la regla práctica
- Usa WP_Query cuando: necesitas paginación, vas a usar funciones de template dentro del loop, necesitas que los filtros de plugins funcionen, o la consulta es compleja.
- Usa get_posts() cuando: solo necesitas un array de posts para procesar datos, la consulta es simple y no necesitas paginación, o estás construyendo algo fuera del ciclo normal de templates.
El error más frecuente — modificar el query principal
Un error muy común es usar WP_Query para modificar el número de posts en el blog principal o en páginas de archivo. La forma correcta de hacerlo es con el hook pre_get_posts, que modifica el query antes de que se ejecute sin crear uno secundario:
// MAL — crea un query secundario innecesario
$query = new WP_Query( array( 'posts_per_page' => 12 ) );
// BIEN — modifica el query principal directamente
add_action( 'pre_get_posts', 'mi_modificar_query_principal' );
function mi_modificar_query_principal( $query ) {
if ( ! is_admin() && $query->is_main_query() && $query->is_home() ) {
$query->set( 'posts_per_page', 12 );
}
}
Usar pre_get_posts es más eficiente porque evita ejecutar dos queries cuando una es suficiente.
Conclusión
La elección entre WP_Query y get_posts() no es una cuestión de preferencia sino de contexto. Para templates con loop y paginación, WP_Query. Para obtener datos sin mostrarlos directamente, get_posts(). Y para modificar el query principal de una página, siempre pre_get_posts.
¿Qué sigue?
- → pre_get_posts: modifica cualquier query sin código duplicado
- → Cómo ordenar y filtrar CPTs con tax_query y meta_query
- → Transients API: cachear resultados de queries costosas
