Kapan menggunakan WP_query (), query_posts () dan pre_get_posts

159

Saya membaca @ nacin's. Anda tidak tahu Query kemarin dan diturunkan sedikit lubang kelinci. Sebelum kemarin, saya (keliru) menggunakan query_posts()untuk semua kebutuhan permintaan saya. Sekarang saya sedikit lebih bijaksana tentang penggunaan WP_Query(), tetapi masih memiliki beberapa daerah abu-abu.

Apa yang saya pikir saya tahu pasti:

Jika saya membuat loop tambahan di mana saja pada halaman — di sidebar, di footer, segala jenis "posting terkait", dll — saya ingin menggunakan WP_Query(). Saya bisa menggunakannya berulang kali pada satu halaman tanpa membahayakan. (Baik?).

Apa yang saya tidak tahu pasti

  1. Kapan saya menggunakan @ nacin ini pre_get_posts vs WP_Query()? Haruskah saya gunakan pre_get_postsuntuk semuanya sekarang?
  2. Ketika saya ingin memodifikasi loop di halaman templat - katakanlah saya ingin memodifikasi halaman arsip taksonomi - apakah saya menghapus if have_posts : while have_posts : the_postbagian dan menulis sendiri WP_Query()? Atau apakah saya memodifikasi output menggunakan pre_get_postsfile functions.php saya?

tl; dr

Aturan dr yang ingin saya tarik dari ini adalah:

  1. Jangan pernah gunakan query_postslagi
  2. Saat menjalankan beberapa kueri pada satu halaman, gunakan WP_Query()
  3. Saat memodifikasi sebuah loop, lakukan ini __________________.

Terima kasih atas kebijaksanaannya

Terry

ps: Saya telah melihat dan membaca: Kapan Anda harus menggunakan WP_Query vs query_posts () vs get_posts ()? Yang menambahkan dimensi lain - get_posts. Tetapi tidak berurusan pre_get_postssama sekali.

saltcod
sumber
@saltcod, sekarang berbeda, WordPress berevolusi, saya menambahkan beberapa komentar dibandingkan dengan jawaban yang diterima di sini .
prosti

Jawaban:

145

Anda benar untuk mengatakan:

Jangan pernah gunakan query_postslagi

pre_get_posts

pre_get_postsadalah filter, untuk mengubah kueri apa pun . Ini paling sering digunakan untuk mengubah hanya 'permintaan utama':

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

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

(Saya juga akan memeriksa yang is_admin()mengembalikan false - meskipun ini mungkin berlebihan.) Permintaan utama muncul di template Anda sebagai:

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

Jika Anda pernah merasa perlu mengedit loop ini - gunakan pre_get_posts. yaitu Jika Anda tergoda untuk menggunakan query_posts()- gunakan pre_get_postssaja.

WP_Query

Permintaan utama adalah contoh penting dari a WP_Query object. WordPress menggunakannya untuk memutuskan template mana yang akan digunakan, misalnya, dan argumen apa pun yang diteruskan ke url (misalnya pagination) semuanya disalurkan ke instance WP_Queryobjek tersebut.

Untuk loop sekunder (misalnya di bilah samping, atau daftar 'posting terkait'), Anda ingin membuat instance WP_Queryobjek Anda sendiri yang terpisah . Misalnya

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

Perhatikan wp_reset_postdata();- ini karena loop sekunder akan menimpa $postvariabel global yang mengidentifikasi 'posting saat ini'. Ini pada dasarnya mengatur ulang bahwa untuk $postkita berada.

get_posts ()

Ini pada dasarnya adalah pembungkus untuk instance WP_Queryobjek yang terpisah. Ini mengembalikan array objek posting. Metode yang digunakan dalam loop di atas tidak lagi tersedia untuk Anda. Ini bukan 'Loop', hanya sebuah array objek posting.

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

Menanggapi pertanyaan Anda

  1. Gunakan pre_get_postsuntuk mengubah kueri utama Anda. Gunakan WP_Queryobjek terpisah (metode 2) untuk loop sekunder di halaman templat.
  2. Jika Anda ingin mengubah kueri loop utama, gunakan pre_get_posts.
Stephen Harris
sumber
Jadi, apakah ada skenario ketika seseorang langsung menuju get_posts () daripada WP_Query?
urok93
@ debanz - ya. Katakan misalnya Anda tidak perlu pagination, atau posting yang lengket di atas - dalam hal get_posts()ini lebih efisien.
Stephen Harris
Tapi bukankah itu menambahkan kueri tambahan di mana kita bisa memodifikasi pre_get_posts untuk memodifikasi kueri utama?
urok93
@ dekrtz - Anda tidak akan menggunakan get_posts()untuk permintaan utama - itu untuk permintaan sekunder.
Stephen Harris
1
@StephenHarris Kanan =) Jika Anda menggunakan next_post () pada objek alih-alih menggunakan the_post, Anda tidak menginjak permintaan global dan tidak perlu ingat untuk menggunakan wp_reset_postdata sesudahnya.
Privat
55

Ada dua konteks yang berbeda untuk loop:

  • loop utama yang terjadi berdasarkan permintaan URL dan diproses sebelum template dimuat
  • loop sekunder yang terjadi dengan cara lain, dipanggil dari file template atau lainnya

Masalahnya query_posts()adalah loop sekunder yang mencoba menjadi yang utama dan gagal total. Jadi, lupakan saja.

Untuk memodifikasi loop utama

  • jangan gunakan query_posts()
  • gunakan pre_get_postsfilter dengan $query->is_main_query()cek
  • secara bergantian gunakan requestfilter (sedikit terlalu kasar sehingga di atas lebih baik)

Untuk menjalankan loop sekunder

Gunakan new WP_Queryatau get_posts()yang cukup banyak dipertukarkan (yang terakhir adalah pembungkus tipis untuk yang pertama).

Membersihkan

Gunakan wp_reset_query()jika Anda menggunakan query_posts()atau mengacaukan global $wp_querysecara langsung - sehingga Anda hampir tidak perlu melakukannya.

Gunakan wp_reset_postdata()jika Anda menggunakan the_post()atau setup_postdata()atau mengacaukan global $postdan perlu mengembalikan keadaan awal hal-hal terkait pasca.

Jarang
sumber
3
Dimaksud pertamawp_reset_postdata()
Gregory
23

Ada skenario yang sah untuk digunakan query_posts($query), misalnya:

  1. Anda ingin menampilkan daftar posting atau posting jenis posting khusus pada halaman (menggunakan templat halaman)

  2. Anda ingin membuat pagination dari posting tersebut berfungsi

Sekarang mengapa Anda ingin menampilkannya di halaman alih-alih menggunakan templat arsip?

  1. Ini lebih intuitif untuk administrator (pelanggan Anda?) - mereka dapat melihat halaman di 'Halaman'

  2. Lebih baik menambahkannya ke menu (tanpa halaman, mereka harus menambahkan url secara langsung)

  3. Jika Anda ingin menampilkan konten tambahan (teks, thumbnail pos, atau konten meta khusus) pada templat, Anda dapat dengan mudah mendapatkannya dari halaman (dan semuanya juga lebih masuk akal bagi pelanggan). Lihat apakah Anda menggunakan templat arsip, Anda harus melakukan hardcode pada konten tambahan atau menggunakan misalnya opsi tema / plugin (yang membuatnya kurang intuitif untuk pelanggan)

Berikut ini contoh kode yang disederhanakan (yang akan ada di templat halaman Anda - mis. Halaman-halaman-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...
}

Sekarang, untuk menjadi sangat jelas, kita bisa menghindari menggunakan di query_posts()sini juga dan menggunakannya WP_Query- seperti:

// ...

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

// ...

Tapi, mengapa kita melakukan itu ketika kita memiliki fungsi kecil yang bagus tersedia untuk itu?

Lukas Pecinka
sumber
1
Brian, terima kasih untuk itu. Saya telah berjuang untuk membuat pre_get_posts bekerja pada halaman di Skenario yang persis Anda jelaskan: klien perlu menambahkan bidang / konten khusus ke halaman arsip, sehingga "halaman" perlu dibuat; klien perlu melihat sesuatu untuk ditambahkan ke menu nav, karena menambahkan tautan khusus lolos darinya; dll +1 dari saya!
Will Lanni
2
Itu juga dapat dilakukan dengan menggunakan "pre_get_posts". Saya melakukan itu untuk memiliki "halaman depan statis" yang mencantumkan jenis posting kustom saya dalam pesanan khusus dan dengan filter kustom. Halaman ini juga diberi nomor halaman. Lihatlah pertanyaan ini untuk melihat cara kerjanya: wordpress.stackexchange.com/questions/30851/... Jadi singkatnya, masih ada skenario yang lebih sah untuk menggunakan query_posts;)
2ndkauboy
1
Karena "Perlu dicatat bahwa menggunakan ini untuk mengganti permintaan utama pada suatu halaman dapat meningkatkan waktu pemuatan halaman, dalam skenario terburuk, lebih dari dua kali lipat jumlah pekerjaan yang dibutuhkan atau lebih. Meskipun mudah digunakan, fungsi ini juga cenderung kebingungan. dan masalah di kemudian hari. " Sumber codex.wordpress.org/Function_Reference/query_posts
Claudiu Creanga
Jawaban ini semua salah. Anda dapat membuat "Halaman" di WP dengan URL yang sama dengan jenis pos kustom. EG jika CPT Anda adalah Pisang, Anda bisa mendapatkan halaman bernama Pisang dengan URL yang sama. Maka Anda akan berakhir dengan siteurl.com/bananas. Selama Anda memiliki arsip-bananas.php di folder tema Anda, maka itu akan menggunakan templat dan "menimpa" halaman itu sebagai gantinya. Seperti yang dinyatakan dalam salah satu komentar lain, menggunakan "metode" ini menciptakan dua kali lipat beban kerja untuk WP, sehingga TIDAK boleh digunakan.
Web Hybrid Hibrid
8

Saya memodifikasi permintaan WordPress dari 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%')"; 
}
T.Todua
sumber
akan tertarik untuk melihat contoh ini tetapi di mana klausa ada di meta khusus.
Andrew Welch
6

Hanya untuk menguraikan beberapa perbaikan pada jawaban yang diterima sejak WordPress berevolusi dari waktu ke waktu dan beberapa hal berbeda sekarang (lima tahun kemudian):

pre_get_postsadalah filter, untuk mengubah kueri apa pun. Ini paling sering digunakan untuk mengubah hanya 'permintaan utama':

Sebenarnya adalah hook tindakan. Bukan filter, dan itu akan memengaruhi kueri apa pun.

Permintaan utama muncul di template Anda sebagai:

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

Sebenarnya ini juga tidak benar. Fungsi ini have_postsmengulangi global $wp_queryobjek yang tidak terkait hanya dengan permintaan utama. global $wp_query;dapat diubah dengan kueri sekunder juga.

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

get_posts ()

Ini pada dasarnya adalah pembungkus untuk instance terpisah dari objek WP_Query.

Sebenarnya, saat ini WP_Queryadalah kelas, jadi kami memiliki instance dari kelas.


Untuk menyimpulkan: Pada saat @StephenHarris menulis kemungkinan besar semua ini benar, tetapi seiring waktu hal-hal di WordPress telah berubah.

prosti
sumber
Secara teknis, itu semua filter di bawah tenda, tindakan hanya filter sederhana. Tapi Anda benar di sini, itu adalah tindakan yang melewati argumen dengan referensi, yang membedakannya dari tindakan yang lebih sederhana.
Milo
get_postsmengembalikan array objek posting, bukan WP_Queryobjek, sehingga memang masih benar. dan WP_Queryselalu menjadi kelas, instance dari kelas = objek.
Milo
Terima kasih, @ Moilo, benar dari beberapa alasan saya memiliki model yang terlalu sederhana di kepala saya.
prosti