WP_query()、query_posts()およびpre_get_postsを使用する場合


159

昨日、@ nacinの 「あなたはQueryがわからない」を読んで、うさぎの穴のクエリを少し送られました。昨日まで、私は(間違って)query_posts()すべてのクエリのニーズに使用していました。今、私はを使用することについて少し賢明ですWP_Query()が、まだいくつかの灰色の領域があります。

私が確かに知っていると思うこと:

ページの任意の場所(サイドバー、フッター、あらゆる種類の「関連する投稿」など)に追加のループを作成する場合は、を使用しWP_Query()ます。害を与えることなく、単一のページで繰り返し使用できます。(右?)。

わからないこと

  1. @ nacin's pre_get_posts vs. はいつ使用しWP_Query()ますか?pre_get_posts今すべてに使用すべきですか?
  2. テンプレートページのループを変更する場合(分類アーカイブページを変更する場合if have_posts : while have_posts : the_postなど)、パーツを削除して独自に作成しWP_Query()ますか?またはpre_get_posts、functions.phpファイルで出力を変更しますか?

tl; dr

これから引き出したいtl; drルールは次のとおりです。

  1. もう使用しquery_postsない
  2. 1つのページで複数のクエリを実行する場合は、 WP_Query()
  3. ループを変更するときは、これを実行してください__________________。

知恵をありがとう

テリー

ps:私は見て、読んだ:あなたはいつWP_Query対query_posts()対get_posts()を使用する必要がありますか?別の次元が追加されます— get_posts。しかし、まったく対処しませんpre_get_posts



@saltcodは今では異なり、WordPressは進化しましたここで受け入れられた答えと比較してコメントをいくつか追加しまし
prosti

回答:


145

あなたは言う権利があります:

もう使用しquery_postsない

pre_get_posts

pre_get_postsクエリを変更するためのフィルターです。最もよく使用されるのは、「メインクエリ」のみを変更する場合です。

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

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

falseis_admin()返すことも確認します-これは冗長かもしれません。)メインクエリは、テンプレートに次のように表示されます。

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

このループを編集する必要があると感じた場合は、使用してくださいpre_get_posts。すなわち、使用したい場合query_posts()- pre_get_posts代わりに使用してください。

WP_Query

メインクエリはの重要なインスタンスですWP_Query object。WordPressはこれを使用して、たとえば、使用するテンプレートを決定し、URLに渡される引数(ページネーションなど)はすべて、WP_Queryオブジェクトのそのインスタンスに送られます。

二次ループ(サイドバーや「関連投稿」リストなど)の場合は、WP_Queryオブジェクトの独自の個別のインスタンスを作成する必要があります。例えば

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

注意wp_reset_postdata();-これは、セカンダリループが$post「現在の投稿」を識別するグローバル変数をオーバーライドするためです。これにより、基本的にリセット$postされます。

get_posts()

これは本質的に、WP_Queryオブジェクトの個別のインスタンスのラッパーです。これは、投稿オブジェクトの配列を返します。上記のループで使用されているメソッドは使用できなくなりました。これは「ループ」ではなく、単に投稿オブジェクトの配列です。

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

あなたの質問に答えて

  1. pre_get_postsメインクエリを変更するために使用します。WP_Queryテンプレートページのセカンダリループに別のオブジェクト(方法2)を使用します。
  2. メインループのクエリを変更する場合は、を使用しますpre_get_posts

WP_Queryではなくget_posts()に直接進むシナリオはありますか?
urok93

@drtanz-はい。たとえば、ページネーションや上部のスティッキーな投稿は必要ないとします-これらのインスタンスでget_posts()はより効率的です。
スティーブンハリス

しかし、pre_get_postsを変更してメインクエリを変更できるだけの追加のクエリを追加しませんか?
urok93

@drtanz- get_posts()メインクエリには使用しません-セカンダリクエリに使用します。
スティーブンハリス

1
@StephenHarris Right =)the_postを使用する代わりにオブジェクトでnext_post()を使用する場合、グローバルクエリを踏むことはなく、後でwp_reset_postdataを使用することを覚えておく必要はありません。
プライベート

55

ループには2つの異なるコンテキストがあります。

  • URL要求に基づいて発生し、テンプレートがロードされる前に処理されるメインループ
  • テンプレートファイルなどから呼び出される他の方法で発生するセカンダリループ

問題query_posts()は、メインループになろうとする二次ループであり、惨めに失敗することです。したがって、存在することを忘れてください。

メインループを変更するには

  • 使用しないでください query_posts()
  • チェックpre_get_posts付きのフィルターを使用$query->is_main_query()する
  • 代わりにrequestフィルターを使用します(少し荒すぎるので上記の方が良いです)

二次ループを実行するには

使用new WP_Queryまたはget_posts()かなりの互換性である(後者は、前者のために薄いラッパーです)。

そうじする

使用wp_reset_query()あなたが使用している場合query_posts()やグローバルで混乱$wp_query直接-あなたがする必要はほとんどないでしょう。

使用wp_reset_postdata()あなたが使用している場合the_post()setup_postdata()、またはグローバルで混乱$postし、ポストに関連するものの初期状態を復元する必要があります。


3
Rarst意味wp_reset_postdata()
グレゴリー

23

を使用するための正当なシナリオがあります。query_posts($query)例:

  1. ページの投稿またはカスタム投稿タイプの投稿のリストを表示したい(ページテンプレートを使用)

  2. これらの投稿のページネーションを機能させたい

アーカイブテンプレートを使用するのではなく、なぜページに表示したいのでしょうか?

  1. 管理者(顧客?)にとってより直感的です-「ページ」でページを見ることができます

  2. メニューに追加する方が良いです(ページなしでは、URLを直接追加する必要があります)

  3. テンプレートに追加のコンテンツ(テキスト、投稿サムネイル、または任意のカスタムメタコンテンツ)を表示したい場合、ページから簡単に取得できます(すべて、顧客にとってもより意味があります)。アーカイブテンプレートを使用したかどうかを確認するには、追加のコンテンツをハードコーディングするか、たとえばテーマ/プラグインオプションを使用する必要があります(これにより、顧客にとって直感的ではなくなります)

以下に簡単なサンプルコードを示します(ページテンプレートにあります-例: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...
}

さて、完全に明確にするために、query_posts()ここでも使用を避け、WP_Query代わりに使用することができます-そのように:

// ...

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

// ...

しかし、使用可能なこのような素敵な小さな機能があるのに、なぜそうするのでしょうか?


1
ブライアン、ありがとう。pre_get_postsをページで正確に動作させるのに苦労してきました。クライアントは、カスタムフィールド/コンテンツをアーカイブページに追加する必要があるため、「ページ」を作成する必要があります。カスタムリンクを追加するとエスケープされるため、クライアントはnavメニューに追加するものを確認する必要があります。等。私から+1!
ウィルラニー

2
これは、「pre_get_posts」を使用して行うこともできます。カスタムポストタイプをカスタムオーダーとカスタムフィルターでリストする「静的フロントページ」を作成しました。このページもページ分割されています。:それはどのように動作するかを確認するには、この質問をチェックしてくださいwordpress.stackexchange.com/questions/30851/... );だから、要するに、query_postsを使用するため、それ以上の正当なシナリオはまだありません
2ndkauboy

1
「これを使用してページのメインクエリを置き換えると、ページの読み込み時間が長くなる可能性があることに注意してください。最悪の場合、必要な作業量が2倍以上になります。使いやすい一方で、この関数は混乱しやすい傾向があります後で問題が発生します。」ソースcodex.wordpress.org/Function_Reference/query_posts
Claudiu Creanga

この答えはあらゆる種類の間違いです。カスタム投稿タイプと同じURLでWPに「ページ」を作成できます。たとえば、CPTがバナナの場合、同じURLのバナナという名前のページを取得できます。その後、siteurl.com / bananasになります。テーマフォルダにarchive-bananas.phpがある限り、テンプレートを使用し、代わりにそのページを「上書き」します。他のコメントの1つで述べたように、この「メソッド」を使用すると、WPのワークロードが2倍になります。したがって、使用しないでください。
ハイブリッドWeb開発

8

functions.phpからWordPressクエリを変更します。

//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%')"; 
}

この例を見て興味がありますが、where句はカスタムメタにあります。
アンドリューウェルチ

6

WordPressは時間の経過とともに進化し、現在(5年後)いくつかの点が異なっているため、受け入れられた回答に対するいくつかの改善点を概説するだけです。

pre_get_postsクエリを変更するためのフィルターです。最もよく使用されるのは、「メインクエリ」のみを変更する場合です。

実際にはアクションフックです。フィルタではなく、クエリに影響します。

メインクエリは、テンプレートに次のように表示されます。

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

実際、これも事実ではありません。この関数は、メインクエリのみに関連しないオブジェクトをhave_posts繰り返します。 二次クエリでも変更される場合があります。global $wp_queryglobal $wp_query;

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

get_posts()

これは本質的に、WP_Queryオブジェクトの個別のインスタンスのラッパーです。

実際、今日WP_Queryはクラスであるため、クラスのインスタンスがあります。


結論:@StephenHarrisが書いた時点では、これらすべてが真実である可能性が最も高いと思われますが、WordPressの状況は徐々に変化しました。


技術的には、すべてが内部のフィルターであり、アクションは単純なフィルターです。しかし、あなたはここで正しいです、それは参照によって引数を渡すアクションであり、それがより単純なアクションとどのように異なるかです。
ミロ

get_postsオブジェクトではなく投稿オブジェクトの配列を返しますWP_Queryので、それでも実際に正しいです。そしてWP_Query、常にクラス=オブジェクトのクラス、インスタンスとなっています。
ミロ

おかげで、@ Milo、何らかの理由で頭の中でモデルを単純化しすぎていました。
prosti
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.