WordPressでホームやアーカイブ毎に表示条件を変える(is_main_query と pre_get_posts フック)

ここ最近のWordPress日本語フォーラムでの投稿で

といったものが多いです。
まとめると「ホームや特定のアーカイブページにおいて、表示条件(件数や並び順など)を変えたい」という内容です。

毎回、大曲さん(@jim0912)のブログ「3.3の新しい関数 is_main_query を使おう」を紹介しつつ、具体例を回答していたのですが、あまりにも似たような投稿(質問)が多いので、少し、具体例を書いておきます。

スポンサー

is_main_query と pre_get_posts フックを使おう

「ホームや特定のアーカイブページにおいて、表示条件(件数や並び順など)を変えたい」というの、WordPressの処理的に言い換えると「メインクエリーを特定の条件であらかじめ抽出し直す」です。

今まで多くのブログで紹介されてきたのがテンプレートファイルに query_posts を使って条件を再定義する方法でした。
もちろんこの方法でも可能ですが、WordPress 3.3から登場した is_main_query 関数と pre_get_posts フックを使う事で、テンプレートファイルにアクセスする前にメインクエリを改変することが可能です。つまり、二重にクエリの呼び出しを行わなくて済むというメリットがあります。
また、query_posts を使う方法だと、変更したいテンプレートファイルすべてに条件変更の記述をしなければなりませんが、is_main_query と pre_get_posts フックを使う方法は functions.php 一箇所に書けば大丈夫というメリットがあります。

なお、本家Codexの query_posts のページでも冒頭で

query_posts() is the easiest, but not preferred or most efficient, way to alter the default query that WordPress uses to display posts. Use query_posts() to display different posts than those that would normally show up at a specific URL. (The preferred way is hooking into ‘pre_get_posts’ and altering the main query that way using is_main_query)

最後の()内で「pre_get_posts フックとis_main_queryを使って書き換える事を推奨します(意訳)」と言っています。

基本的な書き方

基本的な書き方はCodexの is_main_query で紹介されています。

これを functions.phpに記述すると、月別やタグアーカイブ、ホームページ(フロントページ)を表示する際、メインクエリーは特定のカテゴリー(例ではカテゴリーID5)を除外してを取得します。

なお、3.2以前でも可能な方法は先に紹介した大曲さんのブログをご覧ください。

「どんなとき」という条件を変える

日本語で書くとややこしい見出しですが、「ホーム(フロントページ)」「月別アーカイブ」「カテゴリーアーカイブ」といった条件を変更したいときは、先のコードの4行目の if 文の条件を書き換えます。

お気づきかも知れませんが、$query-> のあとに条件分岐タグを加えるだけです。

クエリーの抽出条件を変える

クエリーの抽出条件は先のコードの5行目(4行目から始まる if 文の { } 内)を書き換えます。

ここで $query->set( 'パラメーター名', '内容'); の形で抽出条件を指定していきます。パラメーターは query_posts のパラメーターと同じです。また、複数ある場合は  $query->set( 'パラメーター名', '内容'); の行を増やしていけばOKです。

ちょっと複雑な条件の例

  • ホームページでは通常の投稿の他に、カスタム投稿タイプ(news)の投稿も含める
  • 月別アーカイブではその月の投稿をすべて含める
  • カテゴリーアーカイブでは10件ずつ表示する(管理画面の表示件数は20件)

2013.8.27 追記
Hissy が「WordPressでページ送りが動かないのはどう考えてもquery_postsが悪い!【pre_get_posts、WordPressループまとめ】」というまとめ記事を書かれてました。こちらにたくさんサンプルが載っていますし、かつ予期せぬエラーが起きにくいコードになってましたので、私のサンプルコードも訂正します。

このように条件分岐タグとパラメーターを組み合わせていけば、functions.php のみでも完結します(query_postsの場合はテンプレートファイル毎に記述が必要)。

例えば、通常ではフィード(RSS)に含まれないカスタム投稿を含めることも、上記コードを改変すれば可能です。

すごく便利ですので、ぜひお試しください。

今回の参照先

WordPressでホームやアーカイブ毎に表示条件を変える(is_main_query と pre_get_posts フック)」への11件のフィードバック

  1. 初めまして 質問をさせてください。

    固定ページに設定したホーム、フロントページの時は投稿日付の降順、そのほかの固定ページは投稿を表示させ、それを日付の昇順で表示させようとしています。

    ★フロントページの時->スケジュールの更新情報なので新しい順から。
    ★固定ページの時->スケジュールの実績情報なので古い順から。

    基本は、メインクエリーを昇順にしておき、フロントページだけ降順に変えると出来ると思っていますが。。。

    使用テーマはBizVektorです。

    こちらのページを参考に、下記のコードでやってみましたが、どのページも設定が反映されませんでした。 お忙しいところすみませんがご指摘いただけましたら幸いです。

    is_main_query() ) { // 管理画面以外 かつ メインクエリー
    if ( $query->is_front_page() ) { // 固定ページに設定したホーム、フロントページの時
    // $query->set( ‘posts_per_page’, 10 ); // 10件ずつ表示 オプション
    $query->set( ‘posts_per_page’, -1 ); // すべて表示
    $query->set( ‘orderby’, ‘post_date’ ); //日付で並び替え
    // $query->set( ‘order’, ‘ASC’ ); // 昇順 //オプション
    $query->set( ‘order’, ‘DESC’ ); // 降順
    } elseif ( $query->is_page() ) { // 固定ページ
    // $query->set( ‘posts_per_page’, 10 ); // 10件ずつ表示
    $query->set( ‘posts_per_page’, -1 ); // すべて表示
    $query->set( ‘orderby’, ‘post_date’ ); //日付で並び替え
    $query->set( ‘order’, ‘ASC’ ); // 昇順
    // $query->set( ‘order’, ‘DESC’ ); // 降順 //オプション
    }
    }
    }
    ?>

    1. フロントを固定ページにした場合、メインクエリーは指定した固定ページの情報になります。
      ですので、スケジュール(投稿?)は get_posts を利用してテンプレートファイルに書くのがベストだと思いますし、そうなるとメインのクエリではないので pre_get_posts と is_main_qyeryと使うのではなく、 get_postsの引数で調整する必要があります。
      フロントページ以外の固定ページも同様です。
      もし、フロントページを固定ページにし、投稿ページも固定ページを割り当てていて、そのページの話でしたら、 is_page ではなく、 is_home になります。

  2. ここで、質問をするのは、間違いかもしれませんが…
    一つ質問させてください


    if ( $query->is_home() ) { // ホーム
    $query->set( ‘post_type’, array( ‘post’, ‘news’ ) ); // 投稿とカスタム投稿タイプ news を含める
    return;
    }



    if ( $query->is_post_type_archive(‘hogehoge’) ) {
    $query -> set( ‘posts_per_page’, ‘4’ );
    $query -> set( ‘post_type’ , array( ‘post’, ‘hogehoge’ ));
    return;
    }

    とした場合…

    カスタム投稿「hogehoge」アーカイブのとき、
    表示件数は、4
    投稿と、カスタム投稿タイプ「hogehoge」を含む、
    ということになりますよね?

    しかし、私の場合ですと、index.php が表示されてしまうのです。

    更に、

    $query -> set( ‘post_type’ , array( ‘post’, ‘hogehoge’ ));



    $query -> set( ‘post_type’ , ‘hogehoge’ );

    なら、hogehogeの一覧が表示されるのですが

    $query -> set( ‘post_type’ , ‘post’ );

    ですと、「投稿」の一覧ではなく、index.phpの表示になってしまいます。

    お力添えいただけると幸いです…

    1. ちょっと検証できないのですが、WordPressは要求されたクエリに基づいてどのテンプレートを選択するかを決めます。
      pre_get_posts でクエリが改変されたため(投稿タイプが2種類入っている)、カスタム投稿のアーカイブではない(=index.phpが利用される)、と判断されたのだと思います。
      Debug Barプラグインでクエリは確認できるので、そのあたりを確認してみてはどうでしょうか?

  3. こんちには。質問させて下さい。

    WP_Queryのset()の条件として・・・を除くというものが多いですが、単にIDが3のPOSTを取得した場合は set(‘ID’,10) ではダメなのでしょうか?
    (というか、やってみても効果がなかったので???という感じになりました。)

    ご教示頂ければ幸いです。
    ※ここでの質問が不適切であればご指摘下さい。

    1. WP_Queryのset()の条件として・・・を除くというものが多いですが、単にIDが3のPOSTを取得した場合は set(‘ID’,10) ではダメなのでしょうか?

      パラメーターが違います。
      http://wpdocs.sourceforge.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/WP_Query

      投稿のIDを指定するパラメーターは p です。

      あと、pre_get_posts フックで改変するのはあくまでメインのクエリなので、特定の投稿を取得して加工するなら get_posts などが向いています。

      1. ありがとうございました。
        パラメータ違い…お恥ずかしいです。無事動きました。
        get_postsの件、承知しました。

        色々調べていると、ループ関連は pre_get_posts , get_posts, wp_queryのコンストラクタ利用とあり、なかなかややこしいですね。もう少しいろいろ勉強してみます。
        ありがとうございました。

コメントは受け付けていません。