¿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_Queryes 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();
edelwater
@scribu aunque de nuevo 'get_posts' funcionará aunque no se recomienda: http://core.trac.wordpress.org/ticket/16545
edelwater
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
@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
@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
@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.
Rarst
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.
kaiser
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
@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.
Rarst
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!!
jerclarke
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.
eddiemoya
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 Gayle
@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.
Rarst
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.
Dan Gayle
@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.
Rarst
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
@NoBugs el bucle en esa pregunta está mal y hay una respuesta que explica por qué.
Rarst
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.
racl101
Encontré esta prueba de velocidad entre wp_query y get_posts http://www.wpclocked.com/
Anagio
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.
Rarst
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.
th00ht
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_postses más rápido queWP_Query. El margen depende de la cantidad total de publicaciones del sitio. La razón es queget_postspasa'no_found_rows' => truepor defecto aWP_Query, lo que omite/legalmente rompe la paginación. Con'no_found_rows' => true,WP_Queryobtiene 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_postses realmente un gran desastre.WP_Querydebería usarse para todas las consultas paginadas.get_posts()no se ve influenciado por los filtrosposts_*, mientras queWP_Querysí. La razón es queget_posts, por defecto, pasa'suppress_filters' => trueaWP_Query.get_poststiene algunos parámetros adicionales comoinclude,exclude,numberpostsycategory. Estos parámetros se convierten en parámetros válidos paraWP_Queryantes de ser pasados.includese convierte enpost__in,excludeenpost__not_in,categoryencatynumberpostsenposts_per_page. Solo una nota, todos los parámetros que se pueden pasar aWP_Queryfuncionan conget_posts, puedes ignorar y no usar los parámetros predeterminados deget_posts.get_postsdevuelve solo la propiedad$postsdeWP_Query, mientras queWP_Querydevuelve 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_postsno usa el bucle, sino un bucleforeachpara 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_Queryusa el bucle y las etiquetas de plantilla están disponibles por defecto.get_postspasa'ignore_sticky_posts' => 1aWP_Query, por lo queget_postsignora 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.
InanisAtheos
¡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.
Bullyen
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.