it-swarm-es.com

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

Leí @ nacinNo conoces Query ayer y me enviaron un poco de 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 adicional loops en cualquier lugar de la página, en la barra lateral, en un pie de página, en cualquier tipo de "publicaciones relacionadas", etc., quiero usar WP_Query() . Puedo usar eso repetidamente en una sola página sin ningún daño. (¿Correcto?).

Lo que no sé seguro

  1. ¿Cuándo uso @ nacinpre_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, ¿quito la parte if have_posts : while have_posts : the_post y escribo mi propia 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 use query_posts more
  2. Cuando ejecute varias consultas en una sola página, use WP_Query()
  3. Cuando modifique un bucle, haga esto __________________.

Gracias por cualquier sabiduria

Terry

ps: He visto y leído: ¿Cuándo debería usar WP_Query vs query_posts () vs get_posts ()? Lo que agrega otra dimensión - get_posts . Pero no trata con pre_get_posts en absoluto.

155
saltcod

Tienes razón al decir:

Nunca más uses query_posts

pre_get_posts

pre_get_posts es un filtro, para modificar any query. Se usa con más frecuencia para alterar 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 verificaría que is_admin() devuelva falso, aunque esto puede ser redundante). La consulta principal aparece en tus plantillas como:

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

Si alguna vez siente la necesidad de editar este bucle, use 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();

Observe wp_reset_postdata(); - esto se debe a que el bucle 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 modificar 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.
142
Stephen Harris

Hay dos contextos diferentes para los bucles:

  • main loop que sucede en función de la solicitud de URL y se procesa antes de que se carguen las plantillas
  • secundario bucles 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 miserablemente. Así, olvida que existe.

Modificar bucle principal

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

Para ejecutar bucle secundario

Use new WP_Query o get_posts() que son prácticamente intercambiables (este último es una envoltura delgada para el primero).

Limpiar

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

Usa wp_reset_postdata() si usaste the_post() o setup_postdata() o te metiste con $post global y necesitas restaurar el estado inicial de las cosas relacionadas con el post.

55
Rarst

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. Quieres que la paginación de esos posts 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 '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 también tiene más sentido para el cliente). 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 la plantilla de su 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 ser perfectamente claros, podríamos evitar usar query_posts() aquí también y usar WP_Query en su lugar, 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é haríamos eso cuando tenemos una pequeña función de Niza disponible para ella?

23
Lukas Pecinka

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%')"; 
}
8
T.Todua

Solo para describir algunas mejoras en la respuesta aceptada desde 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 usa con más frecuencia para alterar 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 tus 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 only 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ó muy probablemente todo esto era cierto, pero con el tiempo las cosas en WordPress han cambiado.

6
prosti