• WordPressのメインクエリはquery_posts()で置き換えずpre_get_postsで条件を変える

WordPressのメインクエリはquery_posts()で置き換えずpre_get_postsで条件を変える

公開日: 執筆:佐藤

WordPressの投稿一覧へ表示件数などの条件を加えるなら、テンプレートでquery_posts()を呼び直さず、pre_get_postsアクションでメインクエリを変更します。対象をメインクエリと公開画面へ絞れば、サイドバーなどの別クエリや管理画面まで一緒に変えずに済みます。

query_posts()は、すでにあるメインクエリへ条件を足す関数ではありません。グローバル変数$wp_queryへ新しいWP_Queryを入れ、ループが使うクエリを置き換えます。投稿一覧の条件を変えるだけなら、WordPressが実行する前のクエリを直接変更する方が、処理とページ送りの関係を保ちやすくなります。

メインクエリは表示する画面を決める一つのクエリである

WordPressは、要求されたURLから投稿、固定ページ、カテゴリー一覧、検索結果などを判定し、テンプレートを読み込む前に主な問い合わせを組み立てます。これがメインクエリです。

同じ画面には、関連記事やサイドバーの一覧を取得する別のクエリも置けます。投稿一覧の表示件数を変える処理が、これらすべてへ作用してはいけません。$query->is_main_query()で、WordPressがその画面のために作ったメインクエリだけを選びます。

WordPress 3.9のis_main_query()の実装は、渡されたWP_Queryオブジェクトと$wp_the_queryが同じかを判定します。pre_get_posts内では、グローバル関数is_main_query()ではなく、渡されたオブジェクトの$query->is_main_query()を使います。

query_posts()は元の問い合わせを捨ててもう一度取得する

query_posts()をテンプレートで呼ぶ時点では、WordPressは画面を決めるための問い合わせを実行しています。そこでquery_posts()を呼ぶと、元の結果を使わず、新しいWP_Queryで投稿をもう一度取得します。

WordPress Codexのquery_posts()解説は、メインクエリを別のクエリへ置き換えること、追加のSQL問い合わせが生じること、投稿のページ送りで問題が起きやすいことを挙げています。pagedを適切に渡さなければ、二ページ目でも一ページ目と同じ投稿が出るなど、表示対象とページ番号が合わなくなります。

必要なのは、二つ目の一覧を作ることではなく、最初の一覧の条件を変えることです。pre_get_postsなら、投稿を取得する前のクエリ変数を変更できます。

pre_get_postsでは管理画面、メインクエリ、対象画面を順に絞る

次の例は、投稿一覧のホーム画面だけ、一ページに表示する投稿を12件へ変更します。12件は説明用の設定値です。

function example_change_home_posts_per_page( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    if ( $query->is_home() ) {
        $query->set( 'posts_per_page', 12 );
    }
}
add_action( 'pre_get_posts', 'example_change_home_posts_per_page' );

最初の条件で、管理画面とメインクエリ以外を除外します。その後で、投稿一覧のホーム画面かを判定します。この順序なら、対象外のクエリへ追加の条件判定を重ねません。

pre_get_postsは、公開画面だけでなく管理画面の問い合わせでも実行されます。Codexのpre_get_posts解説も、is_admin()を確認しなければ投稿管理画面の一覧まで変わり得ると説明しています。

フック内の条件判定は渡された$queryに対して行う

pre_get_postsは、実行前のWP_Queryオブジェクトを引数として渡します。次のように、判定と変更を同じ$queryへ向けます。

  • メインクエリか:$query->is_main_query()
  • 投稿一覧のホームか:$query->is_home()
  • 表示件数を変える:$query->set( 'posts_per_page', 12 )

WordPress 3.9のソースは、pre_get_posts内でグローバルな条件分岐関数を使うのではなく、渡されたインスタンスのメソッドを使うよう明記しています。is_main_query()関数をこのフック内で呼ぶと、WordPress 3.7以降は誤った使い方として通知されます。

条件を増やす場合も、先に対象画面を一つ決めます。ホーム、カテゴリー一覧、検索結果を一つの関数でまとめて変えると、どの条件がページ送りへ影響したか追いにくくなります。

一ページ目だけでなく二ページ目と対象外の画面も確認する

コードを追加したら、表示件数だけを見て完了にしません。少なくとも、次の画面を確認します。

  1. 投稿一覧の一ページ目で、指定件数が表示されるか。
  2. 二ページ目へ移動でき、投稿の重複や抜けがないか。
  3. カテゴリー一覧と検索結果の件数が、意図せず変わっていないか。
  4. 管理画面の投稿一覧が、公開画面用の条件で変わっていないか。

ページ送りがある画面では、投稿件数と現在ページを同じメインクエリに持たせることが重要です。query_posts()を残したままpagedだけを継ぎ足すより、置き換え自体をやめ、条件の責任をpre_get_postsへ集めます。

固定ページの本文中へ別の投稿一覧を置く場合は、固定ページを表示するメインクエリまで投稿一覧へ変えません。その一覧はメインクエリの変更とは別の仕事です。まず、変更したい対象が画面そのものか、画面内の追加一覧かを分けてください。

戻る