Quand utiliser WP_query (), query_posts () et pre_get_posts

151

Je lis des des @ nacin Vous ne connaissez pas Query hier et a été envoyé à un trou de lapin interrogateur. Avant hier, j’utilisais (à tort) query_posts() pour tous mes besoins en matière de requêtes. Maintenant, je suis un peu plus sage quant à l'utilisation de WP_Query() , mais certaines zones grises subsistent.

Ce que je pense en être certain:

Si je crée des boucles supplémentaires n'importe où sur une page (dans l'encadré, dans un pied de page, dans n'importe quel type de "messages liés", etc.), je souhaite utiliser WP_Query() . Je peux l'utiliser à plusieurs reprises sur une seule page sans aucun dommage. (droite?).

Ce que je ne sais pas avec certitude

  1. Quand utiliser @ nacin pre_get_posts contre WP_Query() ? Devrais-je utiliser pre_get_posts pour tout faire maintenant?
  2. Lorsque je veux modifier la boucle dans une page de modèle (disons que je veux modifier une page d'archive de taxonomie), dois-je supprimer la partie if have_posts : while have_posts : the_post et écrire mon propre WP_Query() ? Ou puis-je modifier la sortie en utilisant pre_get_posts dans mon fichier functions.php?

tl; dr

Les règles de tl; dr que je voudrais en tirer sont les suivantes:

  1. N'utilisez plus jamais query_posts
  2. Lorsque vous exécutez plusieurs requêtes sur une même page, utilisez WP_Query() .
  3. Lorsque vous modifiez une boucle, procédez comme suit:
posée saltcod 01.05.2012 - 15:04

5 réponses

140

Vous avez raison de dire:

  

N'utilisez plus query_posts

pre_get_posts

pre_get_posts est un filtre permettant de modifier toute requête . Il est le plus souvent utilisé pour modifier uniquement la "requête principale":

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

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

(Je voudrais également vérifier que is_admin() renvoie false - bien que cela puisse être redondant.). La requête principale apparaît dans vos modèles en tant que:

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

Si vous ressentez le besoin de modifier cette boucle, utilisez pre_get_posts . Si vous êtes tenté d'utiliser query_posts() , utilisez plutôt pre_get_posts .

WP_Query

La requête principale est une instance importante d'un WP_Query object . WordPress l'utilise pour décider quel modèle utiliser, par exemple, et tous les arguments passés dans l'URL (par exemple, la pagination) sont tous canalisés vers cette instance de l'objet WP_Query .

Pour les boucles secondaires (par exemple, dans les barres latérales ou dans les listes "publications connexes"), vous voudrez créer votre propre instance distincte de l'objet WP_Query . Par exemple.

$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();

Avis wp_reset_postdata(); - en effet, la boucle secondaire remplacera la variable globale $post qui identifie la "publication actuelle". Cela réinitialise essentiellement le $post sur lequel nous sommes.

get_posts ()

Il s'agit essentiellement d'un wrapper pour une instance distincte d'un objet WP_Query . Cela retourne un tableau d'objets post. Les méthodes utilisées dans la boucle ci-dessus ne vous sont plus disponibles. Ce n'est pas une "boucle", c'est simplement un tableau d'objets 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 réponse à vos questions

  1. Utilisez pre_get_posts pour modifier votre requête principale. Utilisez un objet distinct WP_Query (méthode 2) pour les boucles secondaires dans les pages de modèle.
  2. Si vous souhaitez modifier la requête de la boucle principale, utilisez pre_get_posts .
réponse donnée Stephen Harris 01.05.2012 - 15:27
53

Il existe deux contextes différents pour les boucles:

  • Boucle principale basée sur une requête d'URL et traitée avant le chargement des modèles
  • boucles secondaires qui se produisent de toute autre manière, appelées à partir de fichiers modèles ou autrement

Le problème avec query_posts() est qu’il s’agit d’une boucle secondaire qui tente d’être la boucle principale et qui échoue lamentablement. Oubliez donc qu’il existe.

Pour modifier la boucle principale

  • n'utilisez pas query_posts()
  • utiliser le filtre pre_get_posts avec $query->is_main_query() check
  • utilisez alternativement le filtre request (un peu trop grossier, donc c'est mieux, mieux)

Pour exécuter la boucle secondaire

Utilisez new WP_Query ou get_posts() qui sont à peu près interchangeables (ce dernier est plus fin que l'ancien).

Pour nettoyer

Utilisez wp_reset_query() si vous avez utilisé query_posts() ou si vous vous êtes trompé directement avec le global $wp_query - vous n'aurez donc presque jamais besoin de le faire.

Utilisez wp_reset_postdata() si vous avez utilisé the_post() ou setup_postdata() ou si vous vous êtes trompé avec le $post global et que vous devez restaurer l'état initial des éléments liés à la publication.

    
réponse donnée Rarst 01.05.2012 - 15:27
22

Il existe des scénarios légitimes d'utilisation de query_posts($query) , par exemple:

  1. Vous souhaitez afficher une liste de publications ou des publications de type publication personnalisée sur une page (à l'aide d'un modèle de page)

  2. Vous souhaitez que la pagination de ces publications fonctionne

Maintenant, pourquoi voudriez-vous l'afficher sur une page au lieu d'utiliser un modèle d'archive?

  1. C'est plus intuitif pour un administrateur (votre client?) - il peut voir la page dans les "Pages"

  2. Il vaut mieux l'ajouter aux menus (sans la page, il faudrait ajouter l'URL directement)

  3. Si vous souhaitez afficher du contenu supplémentaire (texte, vignette de publication ou méta-contenu personnalisé) sur le modèle, vous pouvez facilement l'obtenir à partir de la page (et tout cela est plus logique pour le client). Si vous avez utilisé un modèle d’archive, vous devez coder en dur le contenu supplémentaire ou utiliser par exemple des options de thème / plug-in (ce qui le rend moins intuitif pour le client).

Voici un exemple de code simplifié (qui figurerait sur votre modèle de page - par exemple, 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...
}

Maintenant, pour être parfaitement clair, nous pourrions aussi éviter d'utiliser query_posts() ici et utiliser WP_Query - comme ceci:

// ...

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();

// ...

Mais pourquoi ferions-nous cela quand nous aurons une si jolie petite fonction à sa disposition?

    
réponse donnée Lukas Pecinka 16.09.2012 - 09:34
8

Je modifie une requête WordPress à partir de 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%')"; 
}
    
réponse donnée T.Todua 23.01.2015 - 11:11
6

Juste pour souligner quelques améliorations à la réponse acceptée depuis que WordPress a évolué au fil du temps et certaines choses sont différentes maintenant (cinq ans plus tard):

  

pre_get_posts est un filtre permettant de modifier une requête. Il est le plus souvent utilisé pour modifier uniquement la "requête principale":

est en réalité un crochet d’action. Ce n'est pas un filtre et cela affectera toutes les requêtes.

  

La requête principale apparaît dans vos modèles en tant que:

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

En fait, ce n’est pas vrai non plus. La fonction have_posts itère l'objet global $wp_query qui n'est pas lié uniquement à la requête principale. global $wp_query; peut également être modifié avec les requêtes secondaires.

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

get_posts ()

     

Il s'agit essentiellement d'un wrapper pour une instance distincte d'un objet WP_Query.

En fait, de nos jours, WP_Query est une classe, nous avons donc une instance de classe.

Pour conclure: à l'époque où @StephenHarris a très probablement écrit que tout ceci était vrai, mais au fil du temps, les choses ont changé dans WordPress.

    
réponse donnée prosti 28.12.2016 - 02:26

Lire d'autres questions sur les étiquettes