1. ホーム
  2. WordPress

【WordPress】pre_get_posts でメインクエリを変更する方法

Share

「トップページの表示件数を変えたい」「カテゴリーページだけ並び順を変えたい」——こうした調整をしたいとき、テンプレートで WP_Query を新しく作る前に検討したいのが pre_get_posts です。WordPress が本来実行する「メインクエリ」そのものを書き換えるフックで、テンプレートを汚さずに表示内容を変えられます。この記事では pre_get_posts の仕組みと、安全な使い方を解説します。

メインクエリと pre_get_posts

WordPress はページを表示するとき、URL に応じて「どの投稿を取得するか」を自動で組み立てます。これをメインクエリと呼びます。アーカイブページのループ(have_posts() で回すあれ)は、このメインクエリの結果です。

pre_get_posts は、そのメインクエリが実行される直前に呼ばれるアクションフックです。ここでクエリの条件(並び順や件数、対象の投稿タイプなど)を書き換えれば、テンプレート側のループはそのまま使いながら、表示される投稿だけを変更できます。

テンプレート内で new WP_Query() を作る方法でも似たことはできますが、ページネーションが正しく動かなかったり、不要なクエリが増えたりします。メインクエリ自体を変えたいなら pre_get_posts が正攻法です。

基本の使い方

functions.phppre_get_posts に関数を登録します。フックに渡される $query オブジェクト(WP_Query)の set() メソッドで条件を上書きします。次は、トップページの表示件数を5件にする例です。

functions.php
<?php
function my_pre_get_posts( $query ) {
    // 管理画面では動かさない&メインクエリのときだけ処理する
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    // トップページ(記事一覧)のときだけ表示件数を5件に
    if ( $query->is_home() ) {
        $query->set( 'posts_per_page', 5 );
    }
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

ポイントは、フックされた関数の中で必ず「条件を絞る」ことです。これを怠ると、サイト全体や管理画面のクエリまで書き換わってしまいます。詳しくは次の章で説明します。

必ず入れたい2つのガード

pre_get_posts はあらゆるクエリで呼ばれます。対象を絞らないと、意図しない場所まで影響します。次の2つは必ず入れましょう。

is_admin() で管理画面を除外する

pre_get_posts はフロント側だけでなく、管理画面の投稿一覧などでも実行されます。is_admin()true のときは処理を抜けるようにしておかないと、管理画面の表示件数や並び順まで変わってしまいます。

is_main_query() でメインクエリに限定する

サイドバーの「最近の投稿」など、ページ内には複数のサブクエリが存在します。is_main_query() でメインクエリだけに絞らないと、それらのサブクエリまで書き換わってしまいます。この2つのガードはセットで覚えてください。

条件タグで対象ページを絞り込む

「どのページのときに変えるか」は、$query に対する条件判定メソッドで指定します。グローバル関数の is_home() などではなく、$query->is_home() のように $query のメソッドを使うのが安全です。代表的なものを挙げます。

判定対象のページ
$query->is_home()投稿一覧(ブログのトップ)
$query->is_category()カテゴリーアーカイブ
$query->is_search()検索結果ページ
$query->is_post_type_archive()カスタム投稿タイプのアーカイブ

実践例:カテゴリーページの並び順を変える

特定のカテゴリー(ここではスラッグ news)のアーカイブだけ、タイトルの昇順で並べ替える例です。set()orderbyorder を指定します。

functions.php
<?php
function my_news_order( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    // news カテゴリーのアーカイブだけ、タイトル昇順に
    if ( $query->is_category( 'news' ) ) {
        $query->set( 'orderby', 'title' );
        $query->set( 'order', 'ASC' );
    }
}
add_action( 'pre_get_posts', 'my_news_order' );

実践例:検索結果を特定の投稿タイプに限定する

検索結果に固定ページやカスタム投稿が混ざるのを避け、通常の投稿(post)だけに絞る例です。検索ページの調整は pre_get_posts の定番の使い道です。

functions.php
<?php
function my_search_filter( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    // 検索結果は投稿(post)だけに限定する
    if ( $query->is_search() ) {
        $query->set( 'post_type', 'post' );
    }
}
add_action( 'pre_get_posts', 'my_search_filter' );

変更が反映されないときに確認すること

条件タグのタイミングに注意する

条件判定には、グローバルの is_category() ではなく、必ず引数の $query->is_category() を使ってください。pre_get_posts はメインクエリが確定する前に走るため、グローバルの条件タグが正しい結果を返さないことがあります。

set() と get() で上書きする

条件を変えるときは $query->query_vars['posts_per_page'] = 5; のように直接書き換えるより、set() を使うのが安全です。現在の値を確認したいときは $query->get( 'posts_per_page' ) で取得できます。

関数名やフックの綴りを確認する

反映されないときは、add_action() の第1引数が pre_get_posts になっているか、登録した関数名と一致しているかを見直しましょう。条件で return; した後に処理が書かれていないかも確認します。

まとめ

pre_get_posts は、メインクエリそのものを書き換えてアーカイブの表示を調整するためのフックです。

  • テンプレートで WP_Query を新規に作らず、メインクエリを直接調整できる
  • is_admin()$query->is_main_query() のガードを必ず入れる
  • 対象ページは $query->is_home() などのメソッドで絞り込む
  • 条件の上書きは $query->set( キー, 値 ) で行う

表示件数・並び順・対象投稿タイプの調整など、アーカイブまわりのカスタマイズで幅広く使えます。詳しい仕様は WordPress 公式リファレンスの pre_get_posts も参考にしてください。