Cuándo usar WP_query (), query_posts () y pre_get_posts

151

Leí @ nacin No conocía Query ayer y fue enviado un poco como un agujero de conejo. Antes de ayer, estaba (erróneamente) utilizando query_posts() para todas mis necesidades de consulta. Ahora soy un poco más sabio sobre el uso de WP_Query() , pero todavía tengo algunas áreas grises.

Lo que creo que sé con seguridad:

Si estoy haciendo bucles adicionales en cualquier lugar de una página, en la barra lateral, en un pie de página, en cualquier tipo de "publicaciones relacionadas", etc., quiero estar usando WP_Query() . Puedo usar eso repetidamente en una sola página sin ningún daño. (¿Correcto?).

Lo que no sé con seguridad

  1. ¿Cuándo uso @ nacin pre_get_posts vs. WP_Query() ? ¿Debo usar pre_get_posts para todo ahora?
  2. Cuando quiero modificar el bucle en una página de plantilla, digamos que quiero modificar una página de archivo de taxonomía, ¿elimino la parte if have_posts : while have_posts : the_post y escribo mi propia página? WP_Query() ? ¿O modifico la salida usando pre_get_posts en mi archivo functions.php?

tl;dr

Las reglas tl; dr que me gustaría extraer de esto son:

  1. Nunca más uses query_posts
  2. Al ejecutar varias consultas en una sola página, use WP_Query()
  3. Al modificar un bucle, haga esto
pregunta saltcod 01.05.2012 - 15:04

5 respuestas

140

Tienes razón al decir:

  

Nunca más uses query_posts

pre_get_posts

pre_get_posts es un filtro, para modificar cualquier consulta. Se utiliza con más frecuencia para modificar solo la 'consulta principal':

add_action('pre_get_posts','wpse50761_alter_query');
function wpse50761_alter_query($query){

      if( $query->is_main_query() ){
        //Do something to main query
      }
}

(También comprobaría que is_admin() devuelve false , aunque esto puede ser redundante). La consulta principal aparece en sus plantillas como:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

Si alguna vez sientes la necesidad de editar este bucle, usa pre_get_posts . es decir, si está tentado a usar query_posts() , use pre_get_posts en su lugar.

WP_Query

La consulta principal es una instancia importante de un WP_Query object . WordPress lo usa para decidir qué plantilla usar, por ejemplo, y todos los argumentos pasados a la url (por ejemplo, la paginación) se canalizan a esa instancia del objeto WP_Query .

Para bucles secundarios (por ejemplo, en barras laterales o listas de 'publicaciones relacionadas') querrá crear su propia instancia separada del objeto WP_Query . P.ej.

$my_secondary_loop = new WP_Query(...);
if( $my_secondary_loop->have_posts() ):
    while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post();
       //The secondary loop
    endwhile;
endif;
wp_reset_postdata();

Aviso wp_reset_postdata(); : esto se debe a que el ciclo secundario anulará la variable global $post que identifica la "publicación actual". Esto esencialmente restablece eso al $post en el que estamos.

get_posts ()

Esto es esencialmente un contenedor para una instancia separada de un objeto WP_Query . Esto devuelve una matriz de objetos post. Los métodos utilizados en el bucle anterior ya no están disponibles para usted. Esto no es un 'Loop', simplemente una matriz de objetos post.

<ul>
<?php
global $post;
$args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 );
$myposts = get_posts( $args );
foreach( $myposts as $post ) :  setup_postdata($post); ?>
    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endforeach; wp_reset_postdata(); ?>
</ul>

En respuesta a sus preguntas

  1. Use pre_get_posts para alterar su consulta principal. Use un objeto WP_Query separado (método 2) para bucles secundarios en las páginas de la plantilla.
  2. Si desea modificar la consulta del bucle principal, utilice pre_get_posts .
respondido por el Stephen Harris 01.05.2012 - 15:27
53

Hay dos contextos diferentes para los bucles:

  • main que se basa en la solicitud de URL y se procesa antes de que se carguen las plantillas
  • bucles secundarios que ocurren de otra manera, llamados desde archivos de plantilla o de otra manera

El problema con query_posts() es que es un bucle secundario el que intenta ser el principal y falla de forma miserable. Así, olvídate de que existe.

Para modificar el bucle principal

  • no uses query_posts()
  • use pre_get_posts filter con $query->is_main_query() check
  • use alternativamente el filtro request (un poco demasiado aproximado, así que más arriba es mejor)

Para ejecutar un bucle secundario

Utilice new WP_Query o get_posts() , que son bastante intercambiables (este último es una envoltura delgada para el primero).

Para la limpieza

Use wp_reset_query() si usó query_posts() o si ensució directamente con $wp_query , por lo que casi nunca lo necesitará.

Use wp_reset_postdata() si usó the_post() o setup_postdata() o si jugó con $post global y necesita restaurar el estado inicial de las cosas relacionadas con la publicación.

    
respondido por el Rarst 01.05.2012 - 15:27
22

Hay escenarios legítimos para usar query_posts($query) , por ejemplo:

  1. Desea mostrar una lista de publicaciones o publicaciones de tipo publicación personalizada en una página (utilizando una plantilla de página)

  2. Desea que la paginación de esas publicaciones funcione

Ahora, ¿por qué querría mostrarlo en una página en lugar de usar una plantilla de archivo?

  1. Es más intuitivo para un administrador (¿su cliente?): pueden ver la página en las 'Páginas'

  2. Es mejor para agregarlo a los menús (sin la página, tendrían que agregar la url directamente)

  3. Si desea mostrar contenido adicional (texto, miniatura de publicación o cualquier meta contenido personalizado) en la plantilla, puede obtenerlo fácilmente desde la página (y todo esto tiene más sentido para el cliente también). Vea si usó una plantilla de archivo, o bien tendría que codificar el contenido adicional o usar, por ejemplo, opciones de tema / complemento (lo que lo hace menos intuitivo para el cliente)

Aquí hay un código de ejemplo simplificado (que estaría en su plantilla de página, por ejemplo, page-page-of-posts.php):

/**
 * Template Name: Page of Posts
 */

while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

// now we display list of our custom-post-type posts

// first obtain pagination parametres
$paged = 1;
if(get_query_var('paged')) {
  $paged = get_query_var('paged');
} elseif(get_query_var('page')) {
  $paged = get_query_var('page');
}

// query posts and replace the main query (page) with this one (so the pagination works)
query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged));

// pagination
next_posts_link();
previous_posts_link();

// loop
while(have_posts()) {
  the_post();
  the_title(); // your custom-post-type post's title
  the_content(); // // your custom-post-type post's content
}

wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data

// So, now we can display the page-related content again (if we wish so)
while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

Ahora, para que quede perfectamente claro, podríamos evitar usar query_posts() aquí también y usar WP_Query en su lugar, como así:

// ...

global $wp_query;
$wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query

// your custom-post-type loop here

wp_reset_query();

// ...

Pero, ¿por qué lo haríamos cuando tenemos una pequeña función disponible para ella?

    
respondido por el Lukas Pecinka 16.09.2012 - 09:34
8

Modifico la consulta de WordPress desde functions.php:

//unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour)
//so you can use 'add_filter('posts_where', ....);'    OR   modify  "PAGE" query directly into template file

add_action( 'pre_get_posts', 'myFunction' );
function myFunction($query) {
    if ( ! is_admin() && $query->is_main_query() )  {
        if (  $query->is_category ) {
            $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) );
            add_filter( 'posts_where' , 'MyFilterFunction_1' ) && $GLOBALS['call_ok']=1; 
        }
    }
}
function MyFilterFunction_1($where) {
   return (empty($GLOBALS['call_ok']) || !($GLOBALS['call_ok']=false)  ? $where :  $where . " AND ({$GLOBALS['wpdb']->posts}.post_name NOT LIKE 'Journal%')"; 
}
    
respondido por el T.Todua 23.01.2015 - 11:11
6

Solo para describir algunas mejoras en la respuesta aceptada, ya que WordPress evolucionó con el tiempo y algunas cosas son diferentes ahora (cinco años después):

  

pre_get_posts es un filtro, para alterar cualquier consulta. Se utiliza con más frecuencia para modificar solo la 'consulta principal':

En realidad es un gancho de acción. No es un filtro, y afectará a cualquier consulta.

  

La consulta principal aparece en sus plantillas como:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

En realidad, esto tampoco es cierto. La función have_posts itera el objeto global $wp_query que no está relacionado solo con la consulta principal. global $wp_query; también puede modificarse con las consultas secundarias.

function have_posts() {
    global $wp_query;
    return $wp_query->have_posts();
}
  

get_posts ()

     

Esto es esencialmente un contenedor para una instancia separada de un objeto WP_Query.

En realidad, hoy en día WP_Query es una clase, por lo que tenemos una instancia de una clase.

Para concluir: En el momento en que @StephenHarris escribió lo más probable es que todo esto fuera cierto, pero con el tiempo las cosas en WordPress han cambiado.

    
respondido por el prosti 28.12.2016 - 02:26

Lea otras preguntas en las etiquetas