Mengapa query_posts () tidak ditandai sebagai usang?

15

Ada dua query_posts()fungsi yang berbicara secara teknis. Yang satu query_posts()sebenarnya WP_Query::query_posts()dan yang lain ada di ruang global.

Meminta dari kewarasan:

Jika global query_posts()itu "jahat" mengapa tidak usang?

Atau mengapa tidak ditandai sebagai _doing_it_wong.

prosti
sumber
2
Itu pertanyaan yang bagus! Untuk orang lain yang menemukan ini yang tidak tahu mengapa Anda tidak harus menggunakan query_posts (), di sini dan di sini ada beberapa pertanyaan utama yang bagus tentang itu.
Tim Malone

Jawaban:

11

Pertanyaan penting

Menggali Mari ke trio: ::query_posts, ::get_postsdan class WP_Queryuntuk memahami ::query_postslebih baik.

Landasan untuk mendapatkan data di WordPress adalah WP_Querykelasnya. Baik metode ::query_postsdan ::get_postsgunakan kelas itu.

Perhatikan bahwa kelas tersebut WP_Queryjuga berisi metode dengan nama yang sama: WP_Query::query_postsdan WP_Query::get_posts, tetapi kami sebenarnya hanya mempertimbangkan metode global, jadi jangan bingung.

masukkan deskripsi gambar di sini

Memahami WP_Query

Kelas yang dipanggil WP_Querytelah diperkenalkan kembali pada tahun 2004. Semua bidang memiliki tanda ☂ (payung) di mana ada pada tahun 2004. Kolom tambahan ditambahkan kemudian.

Berikut adalah WP_Querystrukturnya:

class WP_Query (as in WordPress v4.7) 
    public $query; 
    public $query_vars = array(); 
    public $tax_query;
    public $meta_query = false;
    public $date_query = false;
    public $queried_object; 
    public $queried_object_id; 
    public $request;
    public $posts; 
    public $post_count = 0; 
    public $current_post = -1; 
    public $in_the_loop = false;
    public $post; 
    public $comments;
    public $comment_count = 0;
    public $current_comment = -1;
    public $comment;
    public $found_posts = 0;
    public $max_num_pages = 0;
    public $max_num_comment_pages = 0;
    public $is_single = false; 
    public $is_preview = false; 
    public $is_page = false; 
    public $is_archive = false; 
    public $is_date = false; 
    public $is_year = false; 
    public $is_month = false; 
    public $is_day = false; 
    public $is_time = false; 
    public $is_author = false; 
    public $is_category = false; 
    public $is_tag = false;
    public $is_tax = false;
    public $is_search = false; 
    public $is_feed = false; 
    public $is_comment_feed = false;
    public $is_trackback = false; 
    public $is_home = false; 
    public $is_404 = false; 
    public $is_embed = false;
    public $is_paged = false;
    public $is_admin = false; 
    public $is_attachment = false;
    public $is_singular = false;
    public $is_robots = false;
    public $is_posts_page = false;
    public $is_post_type_archive = false;
    private $query_vars_hash = false;
    private $query_vars_changed = true;
    public $thumbnails_cached = false;
    private $stopwords;
    private $compat_fields = array('query_vars_hash', 'query_vars_changed');
    private $compat_methods = array('init_query_flags', 'parse_tax_query');
    private function init_query_flags()

WP_Query adalah pisau tentara Swiss.

Beberapa hal tentang WP_Query:

  • itu adalah sesuatu yang dapat Anda kendalikan melalui argumen yang Anda berikan
  • itu serakah secara default
  • itu memegang zat untuk perulangan
  • disimpan di ruang global x2
  • itu bisa primer atau sekunder
  • ini menggunakan kelas pembantu
  • ia memiliki pre_get_postskait berguna
  • bahkan memiliki dukungan untuk loop bersarang
  • itu memegang string kueri SQL
  • itu memegang jumlah hasil
  • itu memegang hasil
  • itu memegang daftar semua argumen permintaan yang mungkin
  • itu memegang bendera templat
  • ...

Saya tidak bisa menjelaskan semua ini, tetapi beberapa di antaranya rumit, jadi mari kita berikan tips singkat.

WP_Query adalah sesuatu yang dapat Anda kendalikan melalui argumen yang Anda berikan

The list of the arguments
---
 attachment
 attachment_id
 author
 author__in
 author__not_in
 author_name
 cache_results
 cat
 category__and
 category__in
 category__not_in
 category_name
 comments_per_page
 day
 embed
 error
 feed
 fields
 hour
 ignore_sticky_posts
 lazy_load_term_meta
 m
 menu_order
 meta_key
 meta_value
 minute
 monthnum
 name
 no_found_rows
 nopaging
 order
 p
 page_id
 paged
 pagename
 post__in
 post__not_in
 post_name__in
 post_parent
 post_parent__in
 post_parent__not_in
 post_type
 posts_per_page
 preview
 s
 second
 sentence
 static
 subpost
 subpost_id
 suppress_filters
 tag
 tag__and
 tag__in
 tag__not_in
 tag_id
 tag_slug__and
 tag_slug__in
 tb
 title
 update_post_meta_cache
 update_post_term_cache
 w
 year

Daftar ini dari WordPress versi 4.7 pasti akan berubah di masa depan.

Ini akan menjadi contoh minimal membuat WP_Queryobjek dari argumen:

// WP_Query arguments
$args = array ( /* arguments*/ );
// creating the WP_Query object
$query = new WP_Query( $args );
// print full list of arguments WP_Query can take
print ( $query->query_vars );

WP_Query serakah

Dibuat atas gagasan, get all you canpengembang WordPress memutuskan untuk mendapatkan semua data yang memungkinkan sejak awal karena ini baik untuk kinerja . Inilah sebabnya mengapa secara default ketika kueri mengambil 10 posting dari database, ia juga akan mendapatkan istilah dan metadata untuk posting ini melalui kueri terpisah. Persyaratan dan metadata akan di-cache (prefetched).

Perhatikan caching hanya untuk satu permintaan seumur hidup.

Anda dapat menonaktifkan caching jika Anda mengatur update_post_meta_cachedan update_post_term_cacheuntuk falsesaat mengatur WP_Queryargumen. Ketika caching dinonaktifkan, data akan diminta dari database hanya berdasarkan permintaan.

Untuk sebagian besar caching blog WordPress berfungsi dengan baik, tetapi ada beberapa kesempatan ketika Anda menonaktifkan caching.

WP_Query menggunakan kelas pembantu

Jika Anda memeriksa WP_Querybidang di sana Anda memiliki tiga ini:

public $tax_query;
public $meta_query;
public $date_query;

Anda bisa membayangkan menambahkan baru di masa depan.

masukkan deskripsi gambar di sini

WP_Query memegang zat untuk pengulangan

Dalam kode ini:

$query = new WP_Query( $args )
if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();

Anda mungkin memperhatikan WP_Querymemiliki substansi yang dapat Anda lakukan berulang kali. Metode pembantu ada di sana juga. Anda cukup mengatur whileloop.

Catatan. fordan whileloop secara semantik setara.

WP_Query primer dan sekunder

Di WordPress Anda memiliki satu kueri primer dan nol atau lebih kueri sekunder .

Dimungkinkan untuk tidak memiliki permintaan utama, tetapi ini berada di luar cakupan artikel ini.

Permintaan primer dikenal sebagai permintaan utama atau permintaan biasa . Permintaan sekunder juga disebut permintaan khusus .

WordPress menggunakan WP_Rewritekelas awal untuk membuat argumen kueri berdasarkan pada URL. Berdasarkan argumen ini, ia menyimpan dua objek identik di ruang global. Kedua hal ini akan menampung permintaan utama.

global $wp_query   @since WordPress 1.5
global $wp_the_query @since WordPress 2.1

Ketika kami mengatakan permintaan utama kami memikirkan variabel-variabel ini. Pertanyaan lain dapat disebut sekunder atau kustom.

Ini sepenuhnya legal untuk menggunakan salah satu global $wp_queryatau $GLOBALS['wp_query'], tetapi menggunakan notasi kedua jauh lebih penting, dan menyimpan mengetikkan garis tambahan di dalam lingkup fungsi.

$GLOBALS['wp_query']dan $GLOBALS['wp_the_query']merupakan objek yang terpisah. $GLOBALS['wp_the_query']harus tetap beku.

WP_Querymemiliki pre_get_postskait berguna .

Ini adalah kait tindakan. Itu akan berlaku untuk contoh apa pun WP_Query . Anda menyebutnya seperti:

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

 if ( is_category() && $query->is_main_query() ) {
    // set your improved arguments
    $query->set( ... );  
    ...  
 }

 return $query;  
});

Pengait ini bagus dan dapat mengubah argumen kueri apa pun.

Inilah yang dapat Anda baca :

Kebakaran setelah objek variabel kueri dibuat, tetapi sebelum kueri aktual dijalankan.

Jadi kait ini adalah manajer argumen tetapi tidak dapat membuat WP_Queryobjek baru . Jika Anda memiliki satu permintaan primer dan satu permintaan sekunder, pre_get_poststidak dapat membuat yang ketiga. Atau jika Anda hanya memiliki satu primer, itu tidak dapat membuat sekunder.

Catatan jika Anda perlu mengubah permintaan utama hanya Anda yang dapat menggunakan requesthook juga.

WP_Query mendukung loop bersarang

Skenario ini dapat terjadi jika Anda menggunakan plugin, dan Anda memanggil fungsi plugin dari template.

Berikut ini contoh showcase WordPress yang memiliki fungsi pembantu bahkan untuk loop bersarang:

global $id;
while ( have_posts() ) : the_post(); 

    // the custom $query
    $query = new WP_Query( array(   'posts_per_page' => 5   ) );    
    if ( $query->have_posts() ) {

        while ( $query->have_posts() ) : $query->the_post();            
            echo '<li>Custom ' . $id . '. ' . get_the_title() . '</li>';
        endwhile;       
    }   

    wp_reset_postdata();
    echo '<li>Main Query ' . $id . '. ' . get_the_title() . '</li>';

endwhile;

Outputnya akan seperti ini sejak saya menginstal data uji unit tema :

Custom 100. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 1. Hello world!

Meskipun saya meminta 5 posting di custom $ query itu akan mengembalikan saya enam, karena posting lengket akan berjalan bersama. Jika tidak ada wp_reset_postdatadalam contoh sebelumnya output akan seperti ini, karena $GLOBALS['post']akan tidak valid.

Custom 1001. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 13. Markup: Title With Special Characters

WP_Querymemiliki wp_reset_queryfungsi

Ini seperti tombol reset. $GLOBALS['wp_the_query']harus dibekukan sepanjang waktu, dan plugin atau tema tidak boleh mengubahnya.

Inilah yang wp_reset_querydilakukan:

function wp_reset_query() {
    $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
    wp_reset_postdata();
}

Keterangan aktif get_posts

get_posts seperti

File: /wp-includes/post.php
1661: function get_posts( $args = null ) {
1662:   $defaults = array(
1663:       'numberposts' => 5,
1664:       'category' => 0, 'orderby' => 'date',
1665:       'order' => 'DESC', 'include' => array(),
1666:       'exclude' => array(), 'meta_key' => '',
1667:       'meta_value' =>'', 'post_type' => 'post',
1668:       'suppress_filters' => true
1669:   );
... // do some argument parsing
1685:   $r['ignore_sticky_posts'] = true;
1686:   $r['no_found_rows'] = true;
1687: 
1688:   $get_posts = new WP_Query;
1689:   return $get_posts->query($r);

Nomor baris dapat berubah di masa mendatang.

Itu hanya pembungkus di sekitar WP_Queryyang mengembalikan posting objek permintaan.

The ignore_sticky_postsset untuk sarana benar tulisan lengket mungkin muncul hanya dalam posisi alami. Tidak akan ada posting yang lengket di depan. Set lainnya no_found_rowske true berarti API database WordPress tidak akan digunakan SQL_CALC_FOUND_ROWSuntuk mengimplementasikan pagination, mengurangi beban pada database untuk mengeksekusi baris yang ditemukan .

Ini berguna saat Anda tidak membutuhkan pagination. Kami mengerti sekarang kami dapat meniru fungsi ini dengan permintaan ini:

$args = array ( 'ignore_sticky_posts' => true, 'no_found_rows' => true);
$query = new WP_Query( $args );
print( $query->request );

Berikut adalah permintaan SQL yang sesuai:

SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10

Bandingkan apa yang kita miliki sekarang dengan permintaan SQL sebelumnya di mana SQL_CALC_FOUND_ROWSada.

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10

Permintaan tanpa SQL_CALC_FOUND_ROWSakan lebih cepat.

Keterangan aktif query_posts

Kiat: Awalnya pada tahun 2004 hanya ada global $wp_query. Pada versi WordPress 2.1 $wp_the_querydatang. Tip: $GLOBALS['wp_query']dan $GLOBALS['wp_the_query']merupakan objek yang terpisah.

query_posts()adalah WP_Querypembungkus. Ini mengembalikan referensi ke WP_Queryobjek utama , dan pada saat yang sama akan mengatur global $wp_query.

File: /wp-includes/query.php
function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Dalam PHP4 semuanya, termasuk objek, diteruskan oleh nilai. query_postsseperti ini:

File: /wp-includes/query.php (WordPress 3.1)
function &query_posts($args) {
    unset($GLOBALS['wp_query']);
    $GLOBALS['wp_query'] =& new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Harap perhatikan dalam skenario umum dengan satu kueri primer dan satu kueri sekunder, kami memiliki tiga variabel berikut:

$GLOBALS['wp_the_query'] 
$GLOBALS['wp_query'] // should be the copy of first one
$custom_query // secondary

Katakanlah masing-masing dari ketiganya membutuhkan 1M memori. Total akan menjadi 3 M memori. Jika kita gunakan query_posts, $GLOBALS['wp_query']akan tidak disetel dan dibuat lagi.

PHP5 + harus pintar mengosongkan $GLOBALS['wp_query']objek, sama seperti di PHP4 kita melakukannya denganunset($GLOBALS['wp_query']);

function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Akibatnya query_postsmenghabiskan 2M memori secara total, sementara get_postsmengkonsumsi 3 M memori.

Catatan dalam query_postskita tidak mengembalikan objek yang sebenarnya, tetapi referensi ke objek.

Dari php.net : Referensi PHP adalah alias, yang memungkinkan dua variabel berbeda untuk menulis ke nilai yang sama. Pada PHP 5, variabel objek tidak mengandung objek itu sendiri sebagai nilai lagi. Ini hanya berisi pengenal objek yang memungkinkan pengakses objek untuk menemukan objek yang sebenarnya. Ketika suatu objek dikirim dengan argumen, dikembalikan atau ditugaskan ke variabel lain, variabel yang berbeda bukan alias: mereka memegang salinan pengidentifikasi, yang menunjuk ke objek yang sama.

Juga di PHP5 + operator assign (=) pintar. Ini akan menggunakan salinan dangkal dan bukan salinan objek keras. Ketika kita menulis seperti ini $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];hanya data yang akan disalin, bukan seluruh objek karena ini berbagi jenis objek yang sama.

Ini salah satu contohnya

print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Akan menghasilkan:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Coba setel ulang kueri:

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Akan menghasilkan:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Anda dapat membuat masalah meskipun Anda menggunakannya WP_Query

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );   
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Tentu saja, solusinya adalah menggunakan wp_reset_queryfungsi lagi.

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Inilah sebabnya saya pikir query_postsmungkin lebih baik dari sudut pandang memori. Tetapi Anda harus selalu melakukan wp_reset_querytrik.

prosti
sumber
10

Saya baru saja membuat tiket trac baru, tiket # 36874 , untuk mengusulkan penghentian query_posts(). Apakah itu akan diterima atau tidak tetap menjadi pertanyaan yang bagus.

Masalah besar sebenarnya query_posts()adalah, masih banyak digunakan oleh plugin dan tema, meskipun ada tulisan yang sangat bagus tentang masalah mengapa Anda TIDAK PERNAH PERNAH menggunakannya. Saya pikir posting paling epik di sini di WPSE adalah yang berikut:

deprecation! == penghapusan , jadi penghentian query_posts()tidak akan menghentikan penggunaannya oleh pengembang berkualitas rendah dan orang-orang pada umumnya yang tidak mengenal WordPress dan yang menggunakan tutorial berkualitas buruk sebagai pedoman. Sama seperti beberapa bukti, berapa banyak pertanyaan yang kita masih mendapatkan di sini di mana orang menggunakan caller_get_postsdi WP_Query? Sudah usang selama bertahun-tahun sekarang.

Namun, fungsi dan argumen yang sudah usang dapat dihapus kapan saja para pengembang inti mau, tetapi ini kemungkinan besar tidak akan pernah terjadi query_posts()karena hal ini akan menghancurkan jutaan situs. Jadi ya, kita mungkin tidak akan pernah melihat penghapusan total query_posts()- yang mungkin mengarah pada fakta bahwa kemungkinan besar itu tidak akan pernah ditinggalkan.

Ini adalah titik awal, tetapi kita harus ingat, mencela sesuatu di WordPress tidak menghentikan penggunaannya.

PEMBARUAN 19 Mei 2016

Tiket yang saya naikkan sekarang ditutup dan ditandai sebagai duplikat untuk tiket berusia 4 tahun , yang ditutup sebagai tidak diperbaiki dan dibuka kembali dan masih tetap terbuka dan belum terselesaikan.

Tampaknya pengembang inti bergantung pada kejahatan kecil yang setia dan lama ini. Semua orang tertarik, inilah tiket lama duplikat 4 tahun

Pieter Goosen
sumber
Mengapa mereka menutup tiket core.trac.wordpress.org/ticket/36874 ? Silakan @PieterGoosen dapatkah Anda memasukkan tautan ke utas ini di tiket core.trac.wordpress.org/ticket/36874 Anda karena pertanyaan ini terkait dengan tiket 1: 1
prosti
@prosti Sepertinya ditandai sebagai duplikat karena masalah ini telah diangkat ... 4 tahun yang lalu ditemukan di sini .
Howdy_McGee
3

[agak kasar]

Ini adalah filosofi inti yang berdiri pada titik ini bahwa tidak ada yang benar-benar usang. Pemberitahuan penghentian, meskipun menyenangkan untuk dimiliki, hanya akan diabaikan jika fungsinya tidak akan benar-benar hilang di beberapa titik. Ada banyak orang yang tidak berkembang bersamaWP_DEBUG dan tidak akan melihat pemberitahuan jika tidak ada kerusakan yang sebenarnya.

OTOH tangan, fungsi ini seperti goto pernyataan. Secara pribadi saya tidak pernah (untuk definisi yang lebih kecil dari yang diharapkan) digunakan gototetapi saya dapat memahami argumen yang menunjuk ke beberapa situasi di mana itu tidak jahat secara default. Sama halnya query_posts, ini adalah cara sederhana untuk mengatur semua global yang diperlukan untuk membuat loop sederhana, dan dapat berguna dalam konteks ajax atau rest-api. Saya tidak akan pernah menggunakannya dalam konteks itu juga, tetapi saya bisa melihat bahwa di sana, ini lebih merupakan masalah gaya pengkodean daripada fungsi yang jahat dengan sendirinya.

Sedikit lebih dalam, masalah utama adalah bahwa global perlu ditetapkan sama sekali. Itu adalah masalah utama, bukan fungsi yang membantu mengaturnya.

Mark Kaplun
sumber
Dan untuk perbandingan, benar-benar query_postslebih lambat daripada kueri sekunder (baca: bukan kueri utama).
prosti
@prosti, karena ia hanya menetapkan dan menjalankan wp_query, berapa lambatnya? yakin ada beberapa overhead tetapi kita mungkin berbicara milidetik di sini. Ini tentu saja mengasumsikan Anda menggunakannya di tempat-tempat di mana WP tidak menyediakan kueri secara default. Di tempat-tempat di mana ia melakukannya adalah buruk, bukan query_postsdirinya sendiri tetapi permintaan tidak berguna yang dilakukan ketika WP sedang memuat
Mark Kaplun