Quando utilizzare WP_query (), query_posts () e pre_get_posts


159

Ho letto @Nacin's You not know Query ieri ed è stato inviato un po 'in una buca di coniglio interrogatorio. Prima di ieri, stavo usando (a torto) query_posts()per tutte le mie esigenze di interrogazione. Ora sono un po 'più saggio sull'uso WP_Query(), ma ho ancora alcune aree grigie.

Quello che penso di sapere per certo:

Se sto facendo loop aggiuntivi in qualsiasi punto di una pagina, nella barra laterale, in un piè di pagina, qualsiasi tipo di "post correlati", ecc., Che desidero utilizzare WP_Query(). Posso usarlo ripetutamente su una singola pagina senza alcun danno. (giusto?).

Quello che non so per certo

  1. Quando uso @ nacin's pre_get_posts vs. WP_Query()? Dovrei usare pre_get_postsper tutto ora?
  2. Quando voglio modificare il ciclo in una pagina modello - diciamo che voglio modificare una pagina di archivio di tassonomia - rimuovo la if have_posts : while have_posts : the_postparte e scrivo la mia WP_Query()? O modifico l'output usando pre_get_postsnel mio file Functions.php?

tl; dr

Le regole di tl; dr che vorrei trarre da questo sono:

  1. Non usarlo query_postspiù
  2. Quando si eseguono più query su una singola pagina, utilizzare WP_Query()
  3. Quando si modifica un loop, procedere __________________.

Grazie per la saggezza

Terry

ps: ho visto e letto: quando dovresti usare WP_Query vs query_posts () vs get_posts ()? Che aggiunge un'altra dimensione - get_posts. Ma non ci si occupa pre_get_postsaffatto.



@saltcod, ora è diverso, WordPress si è evoluto, ho aggiunto alcuni commenti rispetto alla risposta accettata qui .
prosti,

Risposte:


145

Hai ragione a dire:

Non usarlo query_postspiù

pre_get_posts

pre_get_postsè un filtro per modificare qualsiasi query. Viene spesso utilizzato per modificare solo la "query principale":

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

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

(Vorrei anche verificare che is_admin()restituisca false , anche se questo potrebbe essere ridondante.) La query principale viene visualizzata nei tuoi modelli come:

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

Se hai mai sentito la necessità di modificare questo ciclo, usa pre_get_posts. cioè se sei tentato di usare query_posts()- usa pre_get_postsinvece.

WP_Query

La query principale è un'istanza importante di a WP_Query object. WordPress lo utilizza per decidere quale modello utilizzare, ad esempio, e tutti gli argomenti passati nell'URL (ad es. Impaginazione) vengono tutti canalizzati in quell'istanza WP_Querydell'oggetto.

Per i loop secondari (ad es. Nelle barre laterali o negli elenchi di "post correlati") dovrai creare la tua istanza separata WP_Querydell'oggetto. Per esempio

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

Nota wp_reset_postdata();: questo perché il ciclo secondario sovrascriverà la $postvariabile globale che identifica il "post corrente". Questo essenzialmente reimposta quello $postche stiamo vivendo.

get_posts ()

Questo è essenzialmente un wrapper per un'istanza separata di un WP_Queryoggetto. Ciò restituisce una matrice di oggetti post. I metodi utilizzati nel ciclo sopra non sono più disponibili. Questo non è un "Loop", semplicemente un array di oggetti 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>

In risposta alle tue domande

  1. Utilizzare pre_get_postsper modificare la query principale. Utilizzare un WP_Queryoggetto separato (metodo 2) per i loop secondari nelle pagine del modello.
  2. Se si desidera modificare la query del ciclo principale, utilizzare pre_get_posts.

Quindi c'è uno scenario in cui si andrebbe direttamente a get_posts () piuttosto che a WP_Query?
urok93,

@drtanz - sì. Ad esempio, supponiamo che non sia necessario l'impaginazione o che i post appiccicosi siano in alto: in questi casi get_posts()è più efficiente.
Stephen Harris,

Ma ciò non aggiungerebbe una query aggiuntiva in cui potremmo semplicemente modificare pre_get_posts per modificare la query principale?
urok93,

@drtanz - non useresti get_posts()per la query principale - è per le query secondarie.
Stephen Harris,

1
@StephenHarris Right =) Se si utilizza next_post () sull'oggetto anziché utilizzare the_post, non si passa alla query globale e non è necessario ricordare di utilizzare successivamente wp_reset_postdata.
Privateer,

55

Esistono due contesti diversi per i loop:

  • ciclo principale che si verifica in base alla richiesta URL e viene elaborato prima che i modelli vengano caricati
  • loop secondari che si verificano in qualsiasi altro modo, chiamati da file modello o altro

Il problema query_posts()è che è il circuito secondario che cerca di essere il circuito principale e fallisce miseramente. Quindi dimentica che esiste.

Per modificare il loop principale

  • non usare query_posts()
  • utilizzare il pre_get_postsfiltro con $query->is_main_query()segno di spunta
  • usa alternativamente il requestfiltro (un po 'troppo ruvido, quindi sopra è meglio)

Per eseguire il loop secondario

Utilizzare new WP_Queryo get_posts()che sono praticamente intercambiabili (quest'ultimo è un involucro sottile per ex).

Pulire

Usa wp_reset_query()se hai usato query_posts()o incasinato $wp_querydirettamente il globale , quindi non ne avrai quasi mai bisogno.

Usa wp_reset_postdata()se hai usato the_post()o hai setup_postdata()incasinato globale $poste hai bisogno di ripristinare lo stato iniziale delle cose post-correlate.


3
Rarst significavawp_reset_postdata()
Gregory,

23

Esistono scenari legittimi per l'utilizzo query_posts($query), ad esempio:

  1. Desideri visualizzare un elenco di post o post personalizzati di tipo post su una pagina (utilizzando un modello di pagina)

  2. Vuoi far funzionare l'impaginazione di questi post

Ora perché vorresti visualizzarlo su una pagina invece di utilizzare un modello di archivio?

  1. È più intuitivo per un amministratore (il tuo cliente?): Possono vedere la pagina nelle "Pagine"

  2. È meglio per aggiungerlo ai menu (senza la pagina, dovrebbero aggiungere l'URL direttamente)

  3. Se si desidera visualizzare contenuto aggiuntivo (testo, miniatura di post o qualsiasi meta contenuto personalizzato) sul modello, è possibile ottenerlo facilmente dalla pagina (e tutto ha più senso anche per il cliente). Vedi se hai utilizzato un modello di archivio, o dovresti codificare il contenuto aggiuntivo o utilizzare ad esempio le opzioni di temi / plugin (il che lo rende meno intuitivo per il cliente)

Ecco un codice di esempio semplificato (che si trova sul modello di pagina, ad esempio pagina-pagina-di-messaggi.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...
}

Ora, per essere perfettamente chiari, potremmo evitare di usare query_posts()anche qui e usare WP_Queryinvece - in questo modo:

// ...

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

// ...

Ma perché dovremmo farlo quando abbiamo a disposizione una funzione così piccola?


1
Brian, grazie per quello. Ho avuto difficoltà a far funzionare pre_get_posts su una pagina ESATTAMENTE nello scenario che descrivi: il cliente deve aggiungere campi / contenuti personalizzati a quella che altrimenti sarebbe una pagina di archivio, quindi è necessario creare una "pagina"; il client deve vedere qualcosa da aggiungere al menu di navigazione, poiché l'aggiunta di un collegamento personalizzato li sfugge; ecc. +1 da parte mia!
Will Lanni,

2
Questo può essere fatto anche usando "pre_get_posts". L'ho fatto per avere una "prima pagina statica" che elenca i miei tipi di post personalizzati in un ordine personalizzato e con un filtro personalizzato. Questa pagina è anche impaginata. Dai un'occhiata a questa domanda per vedere come funziona: wordpress.stackexchange.com/questions/30851/… Quindi in breve, non esiste ancora uno scenario più legittimo per l'utilizzo di query_posts;)
2ndkauboy,

1
Perché "Va notato che l'utilizzo di questo per sostituire la query principale su una pagina può aumentare i tempi di caricamento della pagina, nei casi peggiori più che raddoppiare la quantità di lavoro necessaria o più. Sebbene sia facile da usare, la funzione è anche soggetta a confusione e problemi in seguito. " Fonte codex.wordpress.org/Function_Reference/query_posts
Claudiu Creanga

Questa risposta è sbagliata. Puoi creare una "Pagina" in WP con lo stesso URL del tipo di post personalizzato. Ad esempio, se il tuo CPT è Bananas, puoi ottenere una pagina chiamata Bananas con lo stesso URL. Quindi finiresti con siteurl.com/bananas. Finché hai archivio-bananas.php nella cartella del tuo tema, utilizzerà invece il modello e "sostituirà" quella pagina. Come affermato in uno degli altri commenti, l'utilizzo di questo "metodo" crea il doppio del carico di lavoro per WP, pertanto NON deve mai essere utilizzato.
Hybrid Web Dev,

8

Modifico la query di WordPress da 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%')"; 
}

sarebbe interessato a vedere questo esempio ma dove la clausola è su meta personalizzata.
Andrew Welch,

6

Solo per delineare alcuni miglioramenti alla risposta accettata da quando WordPress si è evoluto nel tempo e alcune cose sono diverse ora (cinque anni dopo):

pre_get_postsè un filtro per modificare qualsiasi query. Viene spesso utilizzato per modificare solo la "query principale":

In realtà è un gancio di azione. Non è un filtro e influirà su qualsiasi query.

La query principale viene visualizzata nei tuoi modelli come:

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

In realtà, anche questo non è vero. La funzione have_postsesegue l'iterazione global $wp_querydell'oggetto non correlato solo alla query principale. global $wp_query;può essere modificato anche con le query secondarie.

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

get_posts ()

Questo è essenzialmente un wrapper per un'istanza separata di un oggetto WP_Query.

In realtà, al giorno d'oggi WP_Queryè una classe, quindi abbiamo un'istanza di una classe.


Per concludere: All'epoca @StephenHarris ha scritto molto probabilmente tutto ciò era vero, ma nel tempo le cose in WordPress sono state cambiate.


Tecnicamente, sono tutti filtri sotto il cofano, le azioni sono solo un semplice filtro. Ma qui hai ragione, è un'azione che passa un argomento per riferimento, che differisce dalle azioni più semplici.
Milo,

get_postsrestituisce una matrice di oggetti post, non un WP_Queryoggetto, quindi è ancora corretto. ed WP_Queryè sempre stata una classe, istanza di un oggetto class =.
Milo,

Grazie, @Milo, corretto per qualche motivo avevo un modello semplificato nella mia testa.
prosti,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.