Quando usare WP_query(), query_posts() e pre_get_posts

1 mag 2012, 16:04:08
Visualizzazioni: 201K
Voti: 179

Ho letto @nacin's You don't know Query ieri e sono finito in un vortice di informazioni sulle query. Prima di ieri, stavo (erroneamente) usando query_posts() per tutte le mie necessità di query. Ora sono un po' più consapevole sull'uso di WP_Query(), ma ho ancora alcune zone grigie.

Quello che penso di sapere per certo:

Se sto creando loop aggiuntivi in qualsiasi punto della pagina—nella sidebar, nel footer, qualsiasi tipo di "articoli correlati", ecc—dovrei usare WP_Query(). Posso utilizzarlo ripetutamente in una singola pagina senza alcun danno. (giusto?)

Quello che non so per certo

  1. Quando dovrei usare @nacin's pre_get_posts invece di WP_Query()? Dovrei usare pre_get_posts per tutto ora?
  2. Quando voglio modificare il loop in una pagina template — diciamo che voglio modificare una pagina archivio di tassonomia — devo rimuovere la parte if have_posts : while have_posts : the_post e scrivere il mio WP_Query()? Oppure devo modificare l'output usando pre_get_posts nel mio file functions.php?

tl;dr

Le regole tl;dr che vorrei ricavare da questo sono:

  1. Non usare mai più query_posts
  2. Quando si eseguono query multiple in una singola pagina, usare WP_Query()
  3. Quando si modifica un loop, fare questo __________________.

Grazie per qualsiasi consiglio

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 tratta affatto di pre_get_posts.

2
Commenti

Possibile duplicato di Quando dovresti usare WP_Query vs query_posts() vs get_posts()?

dotancohen dotancohen
3 gen 2016 12:29:13

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

prosti prosti
28 dic 2016 03:46:02
Tutte le risposte alla domanda 5
13
164

Hai ragione nel dire:

Non usare più query_posts

pre_get_posts

pre_get_posts è un filtro, per modificare qualsiasi query. È più comunemente usato per alterare solo la 'query principale':

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

      if( $query->is_main_query() ){
        //Fai qualcosa alla query principale
      }
}

(Consiglio anche di verificare che is_admin() restituisca false - anche se potrebbe essere ridondante.). La query principale appare nei tuoi template come:

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

Se senti il bisogno di modificare questo loop - usa pre_get_posts. Ovvero, se sei tentato di usare query_posts() - usa invece pre_get_posts.

WP_Query

La query principale è un'importante istanza di un oggetto WP_Query. WordPress lo usa per decidere quale template utilizzare, ad esempio, e tutti gli argomenti passati nell'url (come la paginazione) vengono incanalati in quell'istanza dell'oggetto WP_Query.

Per loop secondari (ad esempio nelle sidebar, o liste di 'post correlati') vorrai creare una tua istanza separata dell'oggetto WP_Query. Esempio:

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

Nota wp_reset_postdata(); - questo perché il loop secondario sovrascriverà la variabile globale $post che identifica il 'post corrente'. Questo essenzialmente la resetta al $post corrente.

get_posts()

Questo è essenzialmente un wrapper per un'istanza separata di un oggetto WP_Query. Restituisce un array di oggetti post. I metodi usati nel loop sopra non sono più disponibili. Questo non è un 'Loop', ma 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. Usa pre_get_posts per alterare la tua query principale. Usa un oggetto WP_Query separato (metodo 2) per loop secondari nelle pagine template.
  2. Se vuoi alterare la query del loop principale, usa pre_get_posts.
1 mag 2012 16:27:31
Commenti

Quindi c'è qualche scenario in cui sarebbe meglio utilizzare direttamente get_posts() piuttosto che WP_Query?

urok93 urok93
25 ago 2012 19:09:40

@drtanz - sì. Per esempio, quando non hai bisogno della paginazione o dei post sticky in cima - in questi casi get_posts() è più efficiente.

Stephen Harris Stephen Harris
25 ago 2012 21:00:48

Ma questo non aggiungerebbe una query extra quando potremmo semplicemente modificare pre_get_posts per modificare la query principale?

urok93 urok93
26 ago 2012 23:54:06

@drtanz - non useresti get_posts() per la query principale - è pensato per query secondarie.

Stephen Harris Stephen Harris
27 ago 2012 01:42:19

Nel tuo esempio con WP_Query, se cambi $my_secondary_loop->the_post(); con $my_secondary_loop->next_post(); puoi evitare di dover ricordare di usare wp_reset_postdata() purché tu usi $my_post per fare ciò che ti serve.

Privateer Privateer
19 set 2015 01:02:58

@Privateer Non è così, WP_Query::get_posts() imposta global $post;

Stephen Harris Stephen Harris
19 set 2015 11:48:05

@StephenHarris Ho appena esaminato la classe query e non la vedo. Ho controllato soprattutto perché non uso mai wp_reset_postdata perché faccio sempre le query in questo modo. Stai creando un nuovo oggetto e tutti i risultati sono contenuti al suo interno.

Privateer Privateer
19 set 2015 16:14:15

@Privateer - scusa, errore di battitura, WP_Query::the_post(), vedi: https://github.com/WordPress/WordPress/blob/759f3d894ce7d364cf8bfc755e483ac2a6d85653/wp-includes/query.php#L3732

Stephen Harris Stephen Harris
19 set 2015 17:39:19

@StephenHarris Giusto =) Se usi next_post() sull'oggetto invece di usare the_post, non interferisci con la query globale e non devi ricordarti di usare wp_reset_postdata dopo.

Privateer Privateer
19 set 2015 21:01:23

@Privateer Ah, mi scuso, sembra che mi sia confuso. Hai ragione (ma non potresti usare alcuna funzione che si riferisca al globale $post come the_title(), the_content()).

Stephen Harris Stephen Harris
23 set 2015 18:31:04

Vero =) Io non uso mai quelle funzioni quindi non mi mancano.

Privateer Privateer
24 set 2015 19:34:53

@urok93 A volte uso get_posts() quando ho bisogno di ottenere post correlati ACF, specialmente se ce n'è solo uno. Anche se per standardizzare i miei template sto valutando di riscriverli come istanze di WP_Query.

Slam Slam
14 feb 2018 02:15:49

@urok93 Quasi sempre uso get_posts perché è stateless, mentre WP_Query altera chissà quali variabili globali.

tklodd tklodd
12 gen 2022 01:56:53
Mostra i restanti 8 commenti
1
65

Esistono due contesti diversi per i loop:

  • Il loop principale che viene eseguito in base alla richiesta URL e viene processato prima del caricamento dei template
  • I loop secondari che avvengono in qualsiasi altro modo, richiamati dai file template o altrimenti

Il problema con query_posts() è che è un loop secondario che cerca di essere quello principale e fallisce miseramente. Quindi dimenticate che esista.

Per modificare il loop principale

  • Non usare query_posts()
  • Usa il filtro pre_get_posts con il controllo $query->is_main_query()
  • In alternativa usa il filtro request (un po' troppo grezzo quindi l'opzione precedente è migliore)

Per eseguire un loop secondario

Usa new WP_Query o get_posts() che sono praticamente intercambiabili (il secondo è un wrapper leggero del primo).

Per ripulire

Usa wp_reset_query() se hai usato query_posts() o hai modificato direttamente la variabile globale $wp_query - quindi quasi mai ne avrai bisogno.

Usa wp_reset_postdata() se hai usato the_post() o setup_postdata() o hai modificato la variabile globale $post e devi ripristinare lo stato iniziale degli elementi relativi al post.

1 mag 2012 16:27:52
Commenti

Rarst si riferiva a wp_reset_postdata()

Gregory Gregory
1 giu 2012 12:18:31
4
26

Ci sono scenari legittimi per l'utilizzo di query_posts($query), ad esempio:

  1. Vuoi visualizzare un elenco di articoli o articoli di un custom-post-type su una pagina (utilizzando un page template)

  2. Vuoi far funzionare la paginazione di questi articoli

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

  1. È più intuitivo per un amministratore (il tuo cliente?) - possono vedere la pagina in 'Pagine'

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

  3. Se vuoi visualizzare contenuti aggiuntivi (testo, thumbnail dell'articolo o qualsiasi meta contenuto personalizzato) sul template, puoi ottenerlo facilmente dalla pagina (e ha anche più senso per il cliente). Se utilizzassi un template di archivio, dovresti inserire il contenuto aggiuntivo direttamente nel codice o utilizzare ad esempio le opzioni del tema/plugin (il che lo rende meno intuitivo per il cliente)

Ecco un esempio di codice semplificato (che sarebbe nel tuo page template - ad esempio page-page-of-posts.php):

/**
 * Template Name: Pagina di Articoli
 */

while(have_posts()) { // loop principale originale - contenuto della pagina
  the_post();
  the_title(); // titolo della pagina
  the_content(); // contenuto della pagina
  // ecc...
}

// ora visualizziamo l'elenco dei nostri articoli di custom-post-type

// prima otteniamo i parametri di paginazione
$paged = 1;
if(get_query_var('paged')) {
  $paged = get_query_var('paged');
} elseif(get_query_var('page')) {
  $paged = get_query_var('page');
}

// query degli articoli e sostituzione della query principale (pagina) con questa (così la paginazione funziona)
query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged));

// paginazione
next_posts_link();
previous_posts_link();

// loop
while(have_posts()) {
  the_post();
  the_title(); // titolo del tuo articolo di custom-post-type
  the_content(); // contenuto del tuo articolo di custom-post-type
}

wp_reset_query(); // reimposta la query principale (globale $wp_query) alla query originale della pagina (la ottiene dalla variabile globale $wp_the_query) e reimposta i dati dell'articolo

// Quindi, ora possiamo visualizzare nuovamente il contenuto relativo alla pagina (se lo desideriamo)
while(have_posts()) { // loop principale originale - contenuto della pagina
  the_post();
  the_title(); // titolo della pagina
  the_content(); // contenuto della pagina
  // ecc...
}

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

// ...

global $wp_query;
$wp_query = new WP_Query(array('i tuoi parametri di query qui')); // imposta la nuova query personalizzata come query principale

// il tuo loop di custom-post-type qui

wp_reset_query();

// ...

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

16 set 2012 10:34:09
Commenti

Brian, grazie per questo. Ho avuto difficoltà a far funzionare pre_get_posts su una pagina nello SCENARIO ESATTO che descrivi: il cliente ha bisogno di aggiungere campi personalizzati/contenuto a quella che altrimenti sarebbe una pagina di archivio, quindi deve essere creata una "pagina"; il cliente ha bisogno di vedere qualcosa da aggiungere al menu di navigazione, poiché aggiungere un link personalizzato è troppo complicato per loro; ecc. +1 da parte mia!

Will Lanni Will Lanni
13 dic 2012 13:07:49

Questo può essere fatto anche utilizzando "pre_get_posts". L'ho fatto per avere una "pagina frontale statica" che elenca i miei custom post type in un ordine personalizzato e con un filtro personalizzato. Questa pagina è anche impaginata. Dai un'occhiata a questa domanda per vedere come funziona: http://wordpress.stackexchange.com/questions/30851/how-to-use-a-custom-post-type-archive-as-front-page/30854

Quindi, in breve, non c'è ancora uno scenario legittimo per usare query_posts ;)

2ndkauboy 2ndkauboy
12 gen 2015 16:47:27

Perché "Va notato che usare questa funzione per sostituire la query principale su una pagina può aumentare i tempi di caricamento, negli scenari peggiori più che raddoppiando la quantità di lavoro necessaria o anche di più. Sebbene sia facile da usare, la funzione è anche incline a confusione e problemi successivi." Fonte http://codex.wordpress.org/Function_Reference/query_posts

Claudiu Creanga Claudiu Creanga
9 mar 2015 18:31:50

Questa risposta è completamente sbagliata. Puoi creare una "Pagina" in WP con lo stesso URL del Custom post type. Ad esempio, se il tuo CPT si chiama Banane, puoi ottenere una pagina chiamata Banane con lo stesso URL. Quindi finiresti con siteurl.com/banane. Fintanto che hai archive-banane.php nella cartella del tuo tema, allora utilizzerà il template e "sovrascriverà" quella pagina invece. Come affermato in uno degli altri commenti, utilizzare questo "metodo" crea il doppio del lavoro per WP, quindi NON dovrebbe MAI essere usato.

Hybrid Web Dev Hybrid Web Dev
19 giu 2015 23:07:03
1
11

Modifico la query di WordPress da functions.php:

//sfortunatamente, la condizione "IS_PAGE" non funziona in pre_get_posts (è un comportamento di WORDPRESS)
//quindi puoi usare `add_filter('posts_where', ....);`    OPPURE   modificare direttamente la query "PAGE" nel file template

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%')"; 
}
23 gen 2015 12:11:29
Commenti

sarei interessato a vedere questo esempio ma con la clausola where su meta personalizzati.

Andrew Welch Andrew Welch
3 mar 2017 18:20:07
3

Solo per delineare alcuni miglioramenti alla risposta accettata, dato che 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 action hook. Non un filtro, e influenzerà qualsiasi query.

La query principale appare nei tuoi template come:

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

Anche questo non è del tutto vero. La funzione have_posts itera sull'oggetto global $wp_query che non è relativo solo alla query principale. global $wp_query; può essere modificato anche con 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: Al tempo in cui @StephenHarris ha scritto molto probabilmente tutto questo era vero, ma nel tempo le cose in WordPress sono cambiate.

28 dic 2016 03:26:41
Commenti

Tecnicamente, sono tutti filtri sotto il cofano, le azioni sono semplicemente un filtro basilare. Ma hai ragione qui, è un'azione che passa un argomento per riferimento, ed è così che differisce dalle azioni più semplici.

Milo Milo
28 dic 2016 04:16:33

get_posts restituisce un array di oggetti post, non un oggetto WP_Query, quindi è ancora corretto. E WP_Query è sempre stata una classe, un'istanza di una classe = oggetto.

Milo Milo
28 dic 2016 04:16:49

Grazie, @Milo, corretto. Per qualche motivo avevo un modello semplificato nella mia testa.

prosti prosti
28 dic 2016 10:51:18