web-dev-qa-db-fra.com

Quand utiliser WP_query (), query_posts () et pre_get_posts

J'ai lu @ nacin'svous ne connaissez pas Query hier et j'ai été un peu dérouté par un lapin interrogateur. Avant hier, j'étais (à tort) en train d'utiliser query_posts() pour tous mes besoins d'interrogation Maintenant, je suis un peu plus sage quant à l’utilisation de WP_Query() , mais il reste des zones grises.

Ce que je pense savoir avec certitude:

Si je fais supplémentaire} boucles n'importe où sur une page - dans la barre latérale, dans un pied de page, tout type de "messages liés", etc. - je veux utiliser WP_Query() . Je peux utiliser cela à plusieurs reprises sur une seule page sans aucun dommage. (droit?).

Ce que je ne sais pas avec certitude

  1. Quand dois-je utiliser @ nacin'spre_get_posts vs WP_Query() ? Dois-je utiliser pre_get_posts pour tout 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 modifier la sortie à l'aide de pre_get_posts dans mon fichier functions.php?

tl; dr

Les règles que je voudrais en tirer sont les suivantes:

  1. Ne jamais utiliser query_posts plus
  2. Lorsque vous exécutez plusieurs requêtes sur une même page, utilisez WP_Query()
  3. Lorsque vous modifiez une boucle, faites ceci __________________.

Merci pour toute sagesse

Terry

ps: J'ai vu et lu: Quand utiliser WP_Query vs query_posts () vs get_posts ()? qui ajoute une autre dimension - get_posts . Mais ne traite pas de pre_get_posts du tout .

155
saltcod

Vous avez raison de dire:

Ne plus jamais utiliser query_posts

pre_get_posts

pre_get_posts est un filtre permettant de modifier any query. 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 d’éditer 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 de 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 dans cette instance de l'objet WP_Query.

Pour les boucles secondaires (par exemple, dans les barres latérales ou les listes de "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();

Remarquez wp_reset_postdata(); - c'est parce que la boucle secondaire remplacera la variable globale $post qui identifie la "publication actuelle". Cela réinitialise essentiellement cela au $post nous sommes sur.

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", 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 WP_Query séparé (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.
142
Stephen Harris

Il existe deux contextes différents pour les boucles:

  • main boucle qui se produit en fonction de la demande d'URL et qui est traitée avant le chargement des modèles
  • secondary boucles qui se produisent de toute autre manière, appelées à partir de fichiers modèles ou autrement

Le problème avec query_posts() est que c’est une boucle secondaire qui essaie d’être principale et qui échoue lamentablement. Donc oubliez ça existe.

Pour modifier la boucle principale

  • ne pas utiliser query_posts()
  • utiliser le filtre pre_get_posts avec la vérification $query->is_main_query()
  • utilisez alternativement le filtre request (un peu trop rugueux, donc ce qui est en haut est préférable)

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).

Nettoyer

Utilisez wp_reset_query() si vous avez utilisé query_posts() ou si vous avez manipulé le $wp_query global directement - vous n’auriez 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 d'éléments liés à la publication.

55
Rarst

Il existe des scénarios légitimes pour utiliser 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 voulez faire de la pagination de ces messages

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. C'est mieux pour l'ajouter aux menus (sans la page, il faudrait ajouter l'URL directement)

  3. Si vous souhaitez afficher du contenu supplémentaire (texte, miniature de publication ou tout 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 également). Voyez 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/plugin (ce qui le rend moins intuitif pour le client).

Voici un exemple de code simplifié (qui se trouverait 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 alors que nous avons une petite fonction aussi agréable à utiliser?

23
Lukas Pecinka

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

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 n'importe quelle requête. Il est le plus souvent utilisé pour modifier uniquement la "requête principale":

Est en fait un crochet d’action. Ce n'est pas un filtre et cela affectera toute requête.

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é only à 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 écrit le plus probablement, tout cela était vrai, mais au fil du temps, les choses dans WordPress ont été modifiées.

6
prosti