query_posts()が非推奨としてマークされないのはなぜですか?


15

query_posts()技術的に言えば、2つの機能があります。1つquery_posts()は実際にWP_Query::query_posts()あり、もう1つはグローバルスペースにあります。

正気から尋ねる:

グローバルquery_posts()がその「悪」である場合、なぜ非推奨ではないのですか?

または、なぜとしてマークされていません_doing_it_wong


2
それは素晴らしい質問です!query_posts()を使用してはならない理由がわからない他の人のために、ここここにいくつかの良い入門Q&Aがあります。
ティムマローン

回答:


11

本質的な質問

トリオを掘り下げましょう:::query_posts::get_postsそしてよりよくclass WP_Query理解する::query_postsために。

WordPressでデータを取得するための基礎はWP_Queryクラスです。両方のメソッド::query_posts::get_postsそのクラスを使用します。

クラスにWP_Queryは同じ名前のメソッドも含まれていることに注意してください:WP_Query::query_postsWP_Query::get_postsですが、実際にはグローバルメソッドのみを考慮しているため、混同しないでください。

ここに画像の説明を入力してください

を理解する WP_Query

呼び出されたクラスWP_Queryは2004年に導入されました。2004年に存在するすべてのフィールドにmark(傘)マークが付いています。追加のフィールドは後で追加されました。

ここにあるWP_Query構造は:

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 スイスアーミーナイフです。

いくつかのこと WP_Query

  • 渡す引数を介して制御できるものです
  • デフォルトでは貪欲です
  • ループするための物質を保持します
  • グローバルスペースx2に保存されます
  • 一次または二次
  • ヘルパークラスを使用します
  • 便利なpre_get_postsフックがあります
  • ネストされたループもサポートしています
  • SQLクエリ文字列を保持します
  • 結果の数を保持します
  • 結果を保持します
  • 可能なすべてのクエリ引数のリストを保持します
  • テンプレートフラグを保持します
  • ...

これらすべてを説明することはできませんが、これらのいくつかは扱いにくいので、短いヒントを提供しましょう。

WP_Query 渡す引数を介して制御できるものです

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

WordPressバージョン4.7のこのリストは、将来的に変更されることは確かです。

これはWP_Query、引数からオブジェクトを作成する最小限の例です。

// 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 貪欲です

get all you canWordPress開発者はアイデアに基づいて作成されたため、パフォーマンスに優れているため、可能な限りすべてのデータを早期に取得することにしました。これが、デフォルトでクエリがデータベースから10個の投稿を取得するときに、別のクエリを介してこれらの投稿の用語とメタデータも取得する理由です。用語とメタデータはキャッシュされます(プリフェッチされます)。

キャッシングは、単一のリクエストライフタイムのためのものです。

あなたが設定されている場合は、キャッシュを無効にすることができますupdate_post_meta_cacheし、update_post_term_cachefalse設定しながら、WP_Query議論を。キャッシュが無効になっている場合、データはオンデマンドでのみデータベースから要求されます。

大部分のWordPressブログではキャッシュがうまく機能しますが、キャッシュを無効にできる場合があります。

WP_Query ヘルパークラスを使用します

WP_Queryそこでフィールドをチェックした場合、次の3つがあります。

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

将来的に新しいものを追加することを想像できます。

ここに画像の説明を入力してください

WP_Query ループするための物質を保持します

このコードでは:

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

あなたはWP_Query反復することができる物質を持っていることに気付くかもしれません。ヘルパーメソッドもあります。whileループを設定するだけです。

注意。forそしてwhileループは意味的に等価です。

WP_Query 一次および二次

WordPressには、1つのプライマリクエリと0個以上のセカンダリクエリがあります。

プライマリクエリを持たないことも可能ですが、これはこの記事の範囲外です。

メインクエリまたは通常のクエリと呼ばれるプライマリクエリセカンダリクエリは、カスタムクエリとも呼ばれます

WordPressはWP_Rewriteクラスを早期に使用して、URLに基​​づいてクエリ引数を作成します。これらの引数に基づいて、2つの同一のオブジェクトをグローバルスペースに保存します。これらは両方ともメインクエリを保持します。

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

メインクエリを言うとき、これらの変数を考えます。他のクエリは、セカンダリまたはカスタムと呼ばれます。

global $wp_queryまたはの いずれかを使用することは完全に合法です$GLOBALS['wp_query']が、2番目の表記を使用する方がはるかに注目に値し、関数のスコープ内に余分な行を入力する手間が省けます。

$GLOBALS['wp_query']$GLOBALS['wp_the_query']は別のオブジェクトです。$GLOBALS['wp_the_query']凍結したままにしてください。

WP_Query便利なpre_get_postsフックがあります。

これがアクションフックです。すべての WP_Queryインスタンスに適用されます。次のように呼び出します:

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

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

 return $query;  
});

このフックは優れており、クエリ引数を変更できます。

ここにあなたが読むことができるものがあります

クエリ変数オブジェクトが作成された後、実際のクエリが実行される前に発生します。

したがって、このフックは引数マネージャーですが、新しいWP_Queryオブジェクトを作成できません。1つのプライマリクエリと1つのセカンダリクエリpre_get_postsがある場合、3番目のクエリを作成できません。または、プライマリが1つだけの場合は、セカンダリを作成できません。

メインクエリを変更する必要がある場合のみ、requestフックも使用できることに注意してください。

WP_Query ネストされたループをサポート

このシナリオは、プラグインを使用し、テンプレートからプラグイン関数を呼び出す場合に発生する可能性があります。

以下は、WordPressがネストされたループに対してもヘルパー関数を持っているショーケースの例です。

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;

テーマユニットテストデータをインストールしたため、出力は次のようになります

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!

カスタムの$ queryで5つの投稿をリクエストしましたが、スティッキーな投稿が続くため、6つの投稿が返されます。wp_reset_postdata前の例にない場合、出力は$GLOBALS['post']無効になりますので、このようになります。

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_Query持っているwp_reset_query機能を

これはリセットボタンのようなものです。$GLOBALS['wp_the_query']常にフリーズする必要があり、プラグインまたはテーマはそれを決して変更しないでください。

ここに何がありwp_reset_queryます:

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

上の備考 get_posts

get_posts のように見える

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

行番号は将来変更される可能性があります。

それだけであるラッパーの周りのWP_Queryことを返すクエリオブジェクトの記事。

ignore_sticky_posts真の意味スティッキーポストへのセットが唯一の自然な位置に表示される場合があります。前面にスティッキーな投稿はありません。もう1 no_found_rowsつはtrueに設定すると、WordPressデータベースAPIがSQL_CALC_FOUND_ROWSページネーションを実装するために使用しないことを意味し、データベースの負荷を減らして、見つかった行を実行します数。

これは、ページネーションを必要としないときに便利です。このクエリでこの機能を模倣できることがわかりました。

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

対応するSQL要求は次のとおりです。

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

現在の内容を、SQL_CALC_FOUND_ROWS存在する以前のSQL要求と比較します。

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

なしのリクエストSQL_CALC_FOUND_ROWSはより高速になります。

上の備考 query_posts

ヒント:2004年の最初はのみでしたglobal $wp_query。WordPress 2.1のバージョン$wp_the_queryが来ました。ヒント:$GLOBALS['wp_query']$GLOBALS['wp_the_query']は別個のオブジェクトです。

query_posts()あるWP_Queryラッパー。メインWP_Queryオブジェクトへの参照を返し、同時にを設定しglobal $wp_queryます。

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

PHP4では、オブジェクトを含むすべてのものが値渡しされました。query_postsこのようなものでした:

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

1つのプライマリクエリと1つのセカンダリクエリがある典型的なシナリオでは、次の3つの変数があることに注意してください。

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

これら3つのそれぞれが1Mのメモリを消費するとします。合計で3Mのメモリになります。を使用するとquery_posts$GLOBALS['wp_query']設定が解除され、再度作成されます。

PHP5 +は$GLOBALS['wp_query']、PHP4で行ったように、オブジェクトをスマートに空にする必要があります。unset($GLOBALS['wp_query']);

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

その結果query_posts、合計で2Mのメモリをget_posts消費しますが、3Mのメモリを消費します。

query_posts実際のオブジェクトを返すのではなく、オブジェクトへの参照を返すことに注意してください。

php.netから:PHPリファレンスはエイリアスであり、2つの異なる変数が同じ値に書き込むことができます。PHP 5以降、オブジェクト変数にはオブジェクト自体が値として含まれなくなりました。オブジェクトアクセサーが実際のオブジェクトを検索できるようにするオブジェクト識別子のみが含まれています。オブジェクトが引数によって送信、返される、または別の変数に割り当てられるとき、異なる変数はエイリアスではありません。それらは同じオブジェクトを指す識別子のコピーを保持します。

また、PHP5 +では、代入(=)演算子はスマートです。ハードオブジェクトコピーではなく、浅いコピーを使用します。このように記述$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];すると、データは同じオブジェクトタイプを共有するため、オブジェクト全体ではなくデータのみがコピーされます。

一例です

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'] ) ) );

結果:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

クエリをリセットしてください:

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'] ) ) );

結果:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

使用しても問題が発生する可能性があります 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'] ) ) );

もちろん、解決策はwp_reset_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' ) );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

これがquery_posts、メモリの観点からより良いと思う理由です。しかし、常にwp_reset_queryトリックを行う必要があります。


10

の廃止を提案するために、新しいtracチケット、チケット#36874を作成しました。query_posts()。それが受け入れられるかどうかは良い質問のままです。

本当に大きな問題query_posts()は、プラグインやテーマで今でも広く使用されていることです。なぜそれを決して使用しないのかというテーマについては本当に良い文章があります。ここでのWPSEに関する最も壮大な投稿は、次の投稿だと思います。

非推奨!==削除。そのため、非推奨query_posts()にしても、低品質の開発者やWordPressを知らず、低品質のチュートリアルをガイドラインとして使用する一般の人々による使用は停止しません。人々が使うところだけでいくつかの証拠として、我々はまだここにどのように多くの質問を受けるんcaller_get_postsWP_Query?これは、長年にわたって非推奨になっています。

ただし、廃止された関数と引数は、コア開発者が適切であると判断したときにいつでも削除できますがquery_posts()、これは何百万ものサイトを破壊するため、ほとんど発生しません。そうです、おそらく完全に削除されることは決してないでしょうquery_posts()-これはおそらく非推奨になることはほとんどないという事実につながる可能性があります。

ただし、これは出発点ですが、WordPressで何かを非推奨にしても使用を停止することはできません。

2016年5月19日更新

私が調達したチケットは現在クローズされており、4年前のチケットと重複しているとマークされています。まだリニューアルオープンとしては、オープンで未解決のまま。

コア開発者はこの古い忠実な小さな悪に固執しているようです。興味のある方、ここに4年前の複製チケットがあります


なぜ彼らはチケットcore.trac.wordpress.org/ticket/36874を閉じたのですか?あなたのチケットにこのスレッドへのリンクを含めることができ@PieterGoosenくださいcore.trac.wordpress.org/ticket/36874 1:この質問は、チケット1に関連するので
prosti

@prostiこの問題はすでに提起されているため、重複としてマークされたように見えます... 4年前に ここで見つかりました
Howdy_McGee

3

[やや暴言]

現時点で非難されているものはないというのが、現時点での根幹となる哲学です。非推奨の通知は、便利ですが、ある時点で関数が実際に削除されない場合は無視されます。WP_DEBUGonで開発しない人が多く、実際の破損がなければ通知に気付かないでしょう。

OTOHハンド、この関数はgotoステートメントのようなものです。個人的には(予想よりも小さい定義のために)使用しgotoませんでしたが、デフォルトでは悪ではない状況を指し示す議論を理解できます。同じですquery_postsことが、にも当てはまります。これは、単純なループを作成するために必要なすべてのグローバルを設定する簡単な方法であり、ajaxまたはrest-apiコンテキストで役立ちます。私はそれらのコンテキストでも決して使用しませんが、そこでは、それはコーディングスタイルの問題であり、機能自体が悪であることがわかります。

もう少し深く進むと、主な問題はグローバルを設定する必要があることです。それが主な問題であり、設定に役立つ1つの機能ではありません。


そして、比較のために、query_posts二次クエリよりも実際に遅いです(読み取り:メインクエリではありません)。
prosti

@prostiは、wp_queryを設定して実行するだけなので、どれくらい遅くなりますか?確かにいくらかのオーバーヘッドがありますが、ここではおそらくミリ秒を話しています。もちろん、これは、WPが既定でクエリを提供しない場所で使用することを前提としています。それが悪い場所では、query_postsそれ自体ではなく、WPの読み込み時に行われた無用なクエリ
Mark Kaplun
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.