När ska man använda WP_query (), query_posts () och pre_get_posts

Jag läser @nacin ”s Du känner inte frågan igår och skickades ner lite av ett frågande kaninhål. Innan igår använde jag (fel) query_posts() för alla mina frågebehov. Nu är jag lite klokare med att använda WP_Query() , men har fortfarande några gråa områden.

Vad jag tror vet jag med säkerhet:

Om jag gör ytterligare slingor var som helst på en sida – i sidofältet, i en sidfot, alla slags ”relaterade inlägg” osv. – Jag vill använda WP_Query() . Jag kan använda det flera gånger på en enda sida utan att skada det. (rätt?).

Vad jag inte vet med säkerhet

  1. När använder jag @nacin ”s pre_get_posts mot WP_Query() ? Ska jag använda pre_get_posts för allt nu?
  2. När jag vill ändra slingan på en mallsida – låt oss säga att jag vill ändra en taxonomis arkivsida – tar jag bort if have_posts : while have_posts : the_post -delen och skriver min äger WP_Query() ? Eller ändrar jag utdata med pre_get_posts i min funktioner.php-fil?

tl; dr

Tl; dr reglerar Jag skulle vilja dra av detta är:

  1. Använd aldrig query_posts längre
  2. När du kör flera frågor på en sida använder du WP_Query()
  3. När du ändrar en loop gör du det här __________________.

Tack för all visdom

Terry

ps: Jag har sett och läst: När ska du använda WP_Query vs query_posts () vs get_posts ()? Vilket lägger till en annan dimension – get_posts . Men hanterar inte pre_get_posts alls .

Kommentarer

Svar

Du har rätt att säga:

Använd aldrig query_posts längre

pre_get_posts

pre_get_posts är ett filter för att ändra alla fråga. Det används oftast för att bara ändra ”huvudfrågan”:

add_action("pre_get_posts","wpse50761_alter_query"); function wpse50761_alter_query($query){ if( $query->is_main_query() ){ //Do something to main query } } 

(Jag skulle också kontrollera att is_admin() returnerar false – även om detta kan vara överflödigt.). Huvudfrågan visas i dina mallar som:

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

Om du någonsin känner att du behöver redigera den här slingan – använd pre_get_posts. dvs Om du är frestad att använda query_posts() – använd pre_get_posts istället.

WP_Query

Huvudfrågan är en viktig instans av en WP_Query object . WordPress använder den för att bestämma vilken mall som ska användas, till exempel, och alla argument som skickas till webbadressen (t.ex. pagination) kanaliseras alla till den instansen av WP_Query -objektet.

För sekundära slingor (t.ex. i sidofält eller ”relaterade inlägg” -listor) vill du skapa din egen separata instans av WP_Query -objektet. Ex.

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

Observera wp_reset_postdata(); – detta beror på att den sekundära slingan åsidosätter den globala $post variabel som identifierar det ”aktuella inlägget”. Detta återställer i huvudsak det till $post vi är på.

get_posts ()

Detta är i huvudsak ett omslag för en separat instans av ett WP_Query -objekt. Detta returnerar en uppsättning postobjekt. Metoderna som används i slingan ovan är inte längre tillgängliga för dig. ta ”Loop”, helt enkelt en matris med postobjekt.

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

Som svar på dina frågor

  1. Använd pre_get_posts för att ändra din huvudfråga. Använd ett separat WP_Query -objekt (metod 2) för sekundära slingor på mallsidorna.
  2. Om du vill ändra frågan i huvudslingan använder du pre_get_posts.

Kommentarer

  • Finns det något scenario när man går direkt till get_posts () snarare än WP_Query?
  • @drtanz – ja. Säg till exempel att du inte behöver ’ t behöver paginering, eller klibbiga inlägg högst upp – i dessa fall är get_posts() effektivare.
  • Men skulle inte ’ inte lägga till en extra fråga där vi bara kunde ändra pre_get_posts för att ändra huvudfrågan?
  • @drtanz – du skulle inte vilja ’ använder inte get_posts() för huvudfrågan – det är för sekundära frågor.
  • @StephenHarris Right =) Om du använder next_post () på objektet istället för att använda_post, du ’ t steg på den globala frågan och don ’ t behöver komma ihåg att använd wp_reset_postdata efteråt.

Svar

Det finns två olika sammanhang för loopar:

  • huvud loop som sker baserat på URL-begäran och bearbetas innan mallar laddas
  • sekundär

loopar som sker på något annat sätt, kallas från mallfiler eller på annat sätt

Problem med query_posts() är att det är sekundär slinga som försöker vara huvud och misslyckas. Så glöm att det finns.

För att ändra huvudslingan

  • använd inte query_posts()
  • använd pre_get_posts filter med $query->is_main_query() kryssning
  • alternativt använd request filter ( lite för grov så ovan är bättre)

Att köra sekundär slinga

Använd new WP_Query eller get_posts() som är ganska utbytbara (senare är tunn omslag för tidigare).

För att städa upp

Använd wp_reset_query() om du använde query_posts() eller trasslat med global $wp_query direkt – så behöver du nästan aldrig.

Använd wp_reset_postdata() om du använde the_post() eller setup_postdata() eller trasslat med global $post och behöver återställa inledande tillstånd för postrelaterade saker.

Kommentarer

  • Rarst menade wp_reset_postdata()

Answe r

Det finns legitima scenarier för att använda query_posts($query), till exempel:

  1. Du vill visa en lista med inlägg eller anpassade inläggstypsinlägg på en sida (med hjälp av en sidmall)

  2. Du vill att sidindelningen av dessa inlägg ska fungera

Varför skulle du nu vilja visa den på en sida istället för att använda en arkivmall?

  1. Det är mer intuitivt för en administratör (din kund?) – de kan se sidan i ”Sidor”

  2. Det är bättre att lägga till den i menyerna (utan sidan skulle de ha för att lägga till webbadressen direkt)

  3. Om du vill visa ytterligare innehåll (text, inläggsminiatyr eller något annat metainnehåll) i mallen kan du enkelt hämta det från sida (och allt är vettigare för kunden också). Se om du använde en arkivmall, antingen skulle du behöva skriva in ytterligare innehåll eller använda till exempel temainställningar / plugin-alternativ (vilket gör det mindre intuitivt för kunden)

Här ”är en förenklad exempelkod (som skulle finnas i din sidmall – t.ex. 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... }  

För att vara helt tydlig kan vi undvika att använda query_posts() här också och använda WP_Query istället – som så:

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

Men varför skulle vi göra det när vi har en så trevlig liten funktion tillgänglig för det?

Kommentarer

  • Brian, tack för det. Jag ’ har kämpat för att få pre_get_posts att arbeta på en sida i EXAKT det scenario du beskriver: klienten måste lägga till anpassade fält / innehåll till vad som annars skulle vara en arkivsida, så en ” sida ” måste skapas; klienten måste se något att lägga till i nav-menyn, eftersom att lägga till en anpassad länk undgår dem; etc. +1 från mig!
  • Det kan också göras med ” pre_get_posts ”. Jag gjorde det för att ha en ” statisk förstasida ” som listade mina anpassade inläggstyper i en anpassad ordning och med ett anpassat filter. Denna sida är också paginerad. Kolla in den här frågan för att se hur det fungerar: wordpress.stackexchange.com / frågor / 30851 / … Så kort sagt finns det fortfarande inget mer legitimt scenario för att använda query_posts;)
  • Eftersom ” Det bör noteras att användning av detta för att ersätta huvudfrågan på en sida kan öka sidladdningstiderna, i värsta fall kan scenarier mer än fördubbla mängden arbete som krävs eller mer. Även om den är enkel att använda är den också benägen för förvirring och problem senare. ” Källa codex.wordpress.org/Function_Reference/ query_posts
  • Det här svaret är alla typer av fel. Du kan skapa en ” sida ” i WP med samma webbadress som den anpassade posttypen. EG om din CPT är bananer kan du få en sida som heter Bananer med samma URL. Då slutar du ’ med siteurl.com/bananas. Så länge du har arkiv-bananas.php i temamappen använder den mallen och ” åsidosätter ” istället . Som anges i en av de andra kommentarerna skapar dubbla arbetsbelastningen för WP att använda denna ” -metod ”, och bör därför INTE användas.

Svar

Jag ändrar WordPress-frågan från funktioner.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%")"; } 

Kommentarer

  • skulle vara intresserade av att se detta exempel men där klausulen finns på anpassad meta.

Svar

Bara för att beskriva några förbättringar av det accepterade svaret eftersom WordPress utvecklades över tiden och vissa saker är annorlunda nu (fem år senare) :

pre_get_posts är ett filter för att ändra alla frågor. Det används oftast för att bara ändra ”huvudfrågan”:

Det är faktiskt en actionkrok. Inte ett filter och det påverkar alla frågor.

Huvudfrågan visas i dina mallar som:

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

Detta är faktiskt inte heller sant. Funktionen have_posts itererar global $wp_query -objektet som inte är relaterat bara till huvudfrågan. global $wp_query; kan också ändras med de sekundära frågorna.

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

get_posts ()

Detta är i huvudsak ett omslag för en separat instans av ett WP_Query-objekt.

Numera är WP_Query numera en klass, så vi har en förekomst av en klass.


Sammanfattningsvis: Vid den tidpunkten skrev @StephenHarris troligtvis allt detta var sant, men med tiden har saker i WordPress förändrats.

Kommentarer

  • Tekniskt sett är det ’ s alla filter under huven, åtgärder är bara ett enkelt filter. Men du har rätt här, det ’ är en åtgärd som skickar ett argument som referens, vilket skiljer sig från enklare åtgärder.
  • get_posts returnerar en matris med postobjekt, inte ett WP_Query -objekt, så det är verkligen korrekt. och WP_Query har alltid varit en klass, förekomst av ett klass = objekt.
  • Tack, @Milo, korrekt av någon anledning hade jag en förenklad modell i mitt huvud.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *