web-dev-qa-db-ja.com

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

私は読みます @ nacin'sあなたはQuery - を知らない 昨日、ちょっと質問しているうさぎの穴を送りました。昨日の前に、私は(間違って) query_posts() を私のすべての問い合わせの必要性のために使っていました。今、私は WP_Query() を使うことについて少し賢いですが、それでもまだいくつかの灰色の領域があります。

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

ページ上の任意の場所(サイドバー、フッター、任意の種類の「関連記事」など)で追加ループを作成している場合は、 WP_Query() を使用します。私は何の問題もなく単一のページでそれを繰り返し使用することができます。 (右?)。

確実にわからないこと

  1. @ nacinのpre_get_postsWP_Query() の組み合わせはいつですか。今はすべてに pre_get_posts を使うべきですか?
  2. テンプレートページのループを変更したい場合 - 分類アーカイブページを変更したいとしましょう - if have_posts : while have_posts : the_post部分を削除して自分の WP_Query() を書くのですか?それとも、functions.phpファイルで pre_get_posts を使用して出力を変更しますか?

tl; dr

これから導きたいtl; drの規則は次のとおりです。

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

知恵をありがとう

テリー

pS:私は見て読んだことがあります: あなたはいつWP_Queryとquery_posts()とget_posts()を使うべきですか? これは別の次元を追加します - get_posts 。しかし、 pre_get_posts はまったく扱いません。

155
saltcod

あなたが言うのは正しいです:

query_postsをもう使わない

pre_get_posts

pre_get_posts any queryを変更するためのフィルタです。最もよく使われるのは、「メインクエリー」だけを変更することです。

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

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

(私はまたis_admin()falseを返すことを確認します - これは冗長かもしれませんが)。主なクエリはテンプレートに次のように表示されます。

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オブジェクトの独立したインスタンスのラッパーです。これはpostオブジェクトの配列を返します。上記のループで使用されているメソッドはもう使用できません。これは「ループ」ではなく、単にポストオブジェクトの配列です。

<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を使用してください。
142
Stephen Harris

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

  • main ループはURL要求に基づいて行われ、テンプレートがロードされる前に処理されます。
  • secondary テンプレートファイルから呼び出されるなど、他の方法で発生する/その他のループ

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

メインループを修正する

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

二次ループを実行する

ほぼ互換性のあるnew WP_Queryまたはget_posts()を使用してください(後者は前者の薄いラッパーです)。

そうじする

wp_reset_query()を使用した場合、またはグローバル$wp_queryと直接混同した場合は、query_posts()を使用してください。そうする必要はほとんどありません。

wp_reset_postdata()またはthe_post()を使用しているか、グローバル$postと混同していて、投稿関連のものの初期状態を復元する必要がある場合は、setup_postdata()を使用してください。

55
Rarst

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

  1. ページ上の投稿またはカスタム投稿タイプの投稿の一覧を表示する(ページテンプレートを使用)

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

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

  1. 管理者(あなたの顧客?)にとってより直感的です - 彼らは 'Pages'にページを見ることができます

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

// ...

しかし、そのようなNiceという小さな機能が利用可能になったときに、なぜそうするのでしょうか。

23
Lukas Pecinka

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%')"; 
}
8
T.Todua

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

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

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

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

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

実際、これも事実ではありません。関数have_postsは、メインクエリに関連しないonlyglobal $wp_queryオブジェクトを繰り返します。 global $wp_query;は、セカンダリクエリでも変更できます。

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

get_posts()

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

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


結論:@StephenHarrisが書いた時点では、これはすべて真実である可能性が最も高かったが、時間が経つにつれてWordPressの内容は変更された。

6
prosti