¿Cuándo deberías usar WP_Query vs query_posts() vs get_posts()?
Parece que la mitad de los tutoriales en el Codex y en la blogosfera usan query_posts()
y la otra mitad usa WP_Query
.
Todos hacen cosas similares, así que ¿cuándo debería usar uno en lugar de los otros?

query_posts()
es excesivamente simplista y una forma problemática de modificar la consulta principal de una página al reemplazarla con una nueva instancia de la consulta. Es ineficiente (vuelve a ejecutar consultas SQL) y fallará por completo en algunas circunstancias (especialmente al tratar con la paginación de publicaciones). Cualquier código moderno de WP debe usar métodos más confiables, como hacer uso del hookpre_get_posts
, para este propósito. En resumen: nunca uses query_posts().get_posts()
es muy similar en uso y acepta los mismos argumentos (con algunos matices, como valores predeterminados diferentes), pero devuelve un array de publicaciones, no modifica variables globales y es seguro de usar en cualquier lugar.WP_Query
es la clase que impulsa ambos detrás de escena, pero también puedes crear y trabajar con tu propia instancia de ella. Un poco más complejo, menos restricciones, también seguro de usar en cualquier lugar.
Fuente de la imagen: https://www.rarst.net/images/query_functions.png

(1) "y es seguro de usar en cualquier lugar" --> pero no lo uses para el bucle PRINCIPAL. (2) recuerda usar global $query_string; antes de la línea que tiene query_posts();

@scribu aunque de nuevo 'get_posts' funcionará aunque no se recomienda: http://core.trac.wordpress.org/ticket/16545

Creo que query_posts
también es menos eficiente ya que ejecutará consultas adicionales, mientras que si usas solo WP_Query
para tu bucle principal, solo ejecutará la consulta que elijas en WP_Query.

@jjeaton query_posts()
es una pequeña función envoltorio para WP_Query
, lo único adicional que hace (según el diagrama de flujo) es sobrescribir la variable global $wp_query

@Rarst Me refería a esta sección en el Codex para query_posts sin embargo, puede que me equivoque respecto al efecto en el rendimiento. A menos que usar WP_Query en tu archivo de plantilla tenga el mismo resultado (es decir, descartar tu consulta y volver a ejecutarla)

@jjeaton Reemplazar query_posts()
con WP_Query
no hará diferencia en el rendimiento, la consulta original de la página seguirá ejecutándose porque es parte de la carga del núcleo. Esas consultas se ejecutarán incluso si tu archivo de plantilla no tiene ningún bucle.

No puedo quitarme la sensación de que este es el post más genial y votado en WPSE. Debería estar en el Codex también.

Ok, después de mirarlo durante bastante tiempo, creo que a query_posts()
le falta una variable estática que se establece en verdadero después del primer uso y - si se usa dos veces - debería activar _doing_it_wrong();
. Supongo que voy a molestar a los chicos de wp-hacker o trac con esto.

@kaiser bueno... usar query_posts()
dos veces es casi tan malo como una, no importa mucho en mi opinión. :) por cierto Andrew Nacin va a hacer una presentación sobre consultas y dijo que podría proponer algunas mejoras al diagrama de flujo, así que la versión dos podría llegar en algún momento en el futuro.

Voy a añadir mi descripción más clara del "problema de rendimiento de query_posts()": Usar query_posts() o WP_Query dentro de un archivo de plantilla tendrá el mismo costo de rendimiento: la consulta que acabas de realizar. El problema discutido en el artículo del codex es que si realmente quieres reemplazar la consulta, deberías hacerlo filtrando la query_posts() original con el filtro 'parse_query'. De esa manera solo tendrás la consulta original deseable, en lugar de hacer una segunda consulta para reemplazarla torpemente. ¡¡query_posts() NUNCA ES LA SOLUCIÓN!! ¡¡NUNCA!!

Esto no menciona el filtro 'request', que es una excelente manera de modificar la consulta principal. La ventaja sobre query_posts es que esa función elimina la consulta original y genera una nueva, igual que si usaras WP_Query. Al usar el filtro request, estás modificando la consulta original antes de que se envíe. Creo que a eso se refiere @JeremyClarke arriba.

Hay una explicación increíblemente buena sobre query_posts escrita por John James Jacoby en el blog de developer.wordpress.com que supera todas estas respuestas. El punto principal: query_posts
no modifica el bucle principal en absoluto, lo reemplaza después de que ya se ha ejecutado. La mejor manera de modificar el bucle principal es a través de un filtro pre_get_posts
.
http://developer.wordpress.com/2012/05/14/querying-posts-without-query_posts/

@Dan estás confundiendo la implementación técnica y el propósito. query_posts()
sí reemplaza el objeto del loop principal, pero su propósito es modificar el loop principal. Además, aprecio mucho los filtros de loop, pero eso no era lo que preguntaba la pregunta. Hay una pregunta de seguimiento de otra persona sobre ese tema.

La pregunta era "Cuándo deberías usar... query_posts()" y según la lógica presentada por esa entrada de blog y los comentarios anteriores, la respuesta es probablemente nunca.

@Manny Fleurmond conceptualmente query_posts()
es un intento de simplificar los conceptos del bucle principal al nivel de una etiqueta de plantilla (facilidad que es uno de los puntos fuertes de la popularidad de WP). La tarea simplemente resultó ser demasiado para que una etiqueta de plantilla pudiera lograrlo. Los desarrolladores principales mencionaron la posibilidad de que se deprecie, pero no creo que haya habido una decisión al respecto todavía.

En realidad no puedes "usar en cualquier lugar" WP_Query(), acabo de intentarlo y todavía falla en $thequery->have_posts(), recursión infinita, ver http://wordpress.stackexchange.com/questions/34270

@NoBugs el bucle en esa pregunta está mal y hay una respuesta que explica por qué.

Agghh gracias por esto. Finalmente, algo tiene sentido. En serio, WordPress y su pésima documentación. No sé cómo un software tan enredado y con malos estándares de codificación se volvió tan popular.

Encontré esta prueba de velocidad entre wp_query y get_posts http://www.wpclocked.com/

Confiaría en tal prueba... exactamente nada. :) La función es un envoltorio muy delgado, cualquier diferencia vendrá de ligeras variaciones en los argumentos y/o hooks.

No hay necesidad de emociones, query_posts() es una función con efectos secundarios: establece una variable global. WordPress está plagado de funciones con efectos secundarios. Esto no es un problema de rendimiento sino un problema de calidad de código. Mira https://developer.wordpress.org/reference/functions/query_posts/ y observa lo que hace query_posts. Usa WP_Query a menos que quieras alterar variables globales.

query_posts
- Nunca deberías usar query_posts
. Además de lo que @Rarst ha mencionado, el gran problema con query_posts
es que rompe el objeto de consulta principal (almacenado en $wp_query
). Muchos plugins y código personalizado dependen del objeto de consulta principal, por lo que romperlo significa que estás afectando la funcionalidad de plugins y código personalizado. Una de estas funciones es la importante función de paginación, así que si rompes la consulta principal, rompes la paginación.
Para demostrar lo malo que es query_posts
, en cualquier plantilla, haz lo siguiente y compara los resultados:
var_dump( $wp_query );
query_posts( '&posts_per_page=-1' );
var_dump( $wp_query );
get_posts
y WP_Query
son la forma correcta de construir consultas secundarias (como publicaciones relacionadas, sliders, contenido destacado y contenido en páginas de inicio estáticas). Cabe destacar que no deberías usar ninguno de los dos en lugar de la consulta principal en la página de inicio, página individual o cualquier tipo de página de archivo, ya que romperá la funcionalidad de la página. Si necesitas modificar la consulta principal, usa pre_get_posts
para hacerlo, no una consulta personalizada. (ACTUALIZACIÓN: Para páginas de inicio estáticas y páginas verdaderas, consulta Usando pre_get_posts en páginas verdaderas y páginas de inicio estáticas*)
En esencia, WP_Query
es utilizado por la consulta principal y también por get_posts
, pero aunque get_posts()
usa WP_Query
, hay algunas diferencias:
get_posts
es más rápido queWP_Query
. El margen depende de la cantidad total de publicaciones del sitio. La razón es queget_posts
pasa'no_found_rows' => true
por defecto aWP_Query
, lo que omite/legalmente rompe la paginación. Con'no_found_rows' => true
,WP_Query
obtiene la cantidad de publicaciones consultadas y luego se detiene, mientras que, por defecto, sigue buscando todas las publicaciones que coincidan con la consulta para calcular la paginación.Por esta razón,
get_posts()
debería usarse solo para consultas no paginadas. Paginarget_posts
es realmente un gran desastre.WP_Query
debería usarse para todas las consultas paginadas.get_posts()
no se ve influenciado por los filtrosposts_*
, mientras queWP_Query
sí. La razón es queget_posts
, por defecto, pasa'suppress_filters' => true
aWP_Query
.get_posts
tiene algunos parámetros adicionales comoinclude
,exclude
,numberposts
ycategory
. Estos parámetros se convierten en parámetros válidos paraWP_Query
antes de ser pasados.include
se convierte enpost__in
,exclude
enpost__not_in
,category
encat
ynumberposts
enposts_per_page
. Solo una nota, todos los parámetros que se pueden pasar aWP_Query
funcionan conget_posts
, puedes ignorar y no usar los parámetros predeterminados deget_posts
.get_posts
devuelve solo la propiedad$posts
deWP_Query
, mientras queWP_Query
devuelve el objeto completo. Este objeto es bastante útil cuando se trata de condicionales, paginación y otra información útil que se puede usar dentro del bucle.get_posts
no usa el bucle, sino un bucleforeach
para mostrar las publicaciones. Además, no hay etiquetas de plantilla disponibles por defecto. Se debe usarsetup_postdata( $post )
para que las etiquetas de plantilla estén disponibles.WP_Query
usa el bucle y las etiquetas de plantilla están disponibles por defecto.get_posts
pasa'ignore_sticky_posts' => 1
aWP_Query
, por lo queget_posts
ignora las publicaciones fijas por defecto.
En base a lo anterior, decidir si usar get_posts
o WP_Query
depende de ti y de lo que realmente necesites de la consulta. Lo anterior debería guiarte en tu elección.

Ojalá pudiera marcar respuestas como favoritas. Esto explica mucho.

¡Excelente explicación!
"get_posts() solo debería usarse para consultas no paginadas. Paginar get_posts es realmente un gran desorden. WP_Query debería usarse para todas las consultas paginadas" es básicamente todo lo que alguien necesita saber en mi opinión.

La diferencia básica es que query_posts()
realmente solo sirve para modificar el Loop actual. Una vez que hayas terminado, es necesario restablecer el loop y dejarlo seguir su curso. Este método también es un poco más fácil de entender, simplemente porque tu "consulta" es básicamente una cadena de URL que pasas a la función, así:
query_posts('meta_key=color&meta_value=blue');
Por otro lado, WP_Query
es más una herramienta de propósito general, y se parece más a escribir consultas MySQL directamente que query_posts()
. También puedes usarlo en cualquier lugar (no solo en el Loop) y no interfiere con ninguna consulta de publicaciones que se esté ejecutando actualmente.
Por lo general, tiendo a usar WP_Query
con más frecuencia. En realidad, todo dependerá de tu caso específico.

Simplemente no hay necesidad de usar query_posts()
. Todo lo que hace es instanciar un nuevo objeto WP_Query y reasignar ese nuevo objeto a global wp_query
.
Como referencia, lo siguiente es la función real query_posts()
.
function query_posts($query) {
$GLOBALS['wp_query'] = new WP_Query();
return $GLOBALS['wp_query']->query($query);
}
Instancia tu propio objeto WP_Query si deseas crear un script de consulta personalizado en profundidad. O usa get_posts()
si solo necesitas hacer algunas manipulaciones menores aquí y allá.
En cualquier caso, recomiendo encarecidamente que te hagas un favor y vayas a wp_includes/query.php
y examines la clase WP_Query
.

- query_posts(): podría usarse en un único caso si necesitas modificar la consulta principal. Establece muchas variables globales;
- get_posts(): es muy similar en mecánica y acepta los mismos argumentos, pero devuelve un array de posts
- WP_Query: puedes crear y trabajar con tu propio objeto de esta clase. Un poco más complejo, con menos restricciones, es seguro usarlo en cualquier lugar.
