ブログ一覧に戻る
Wordpress

WordPressのクエリを正しく扱う ― pre_get_postsの仕組みと使い分け

7 min read
M

Masaki Oba

代表取締役・WordPress運用スペシャリスト

目次

WordPressのクエリを正しく扱う ― pre_get_postsの仕組みと使い分け

メインクエリとサブクエリ

WordPressのクエリを理解するには、まず「メインクエリ」と「サブクエリ」の違いを知っておく必要があります。

メインクエリ

WordPressはURLに応じて、表示に必要な投稿データを自動的にデータベースから取得します。この自動的な問い合わせがメインクエリです。

たとえば投稿の詳細ページにアクセスすると、メインクエリにはその記事のデータが格納されます。アーカイブページなら、該当する投稿の一覧が格納されます。テンプレート内でおなじみの have_posts() / the_post() のループは、このメインクエリのデータを出力しています。

if ( have_posts() ) {
    while ( have_posts() ) {
        the_post();
        // 記事の出力処理
    }
}

サブクエリ

メインクエリとは別に、任意の条件で投稿を取得したい場合に使うのがサブクエリです。サイドバーに「最新の5件」を表示したり、記事下に「関連記事」を出したりする場面で使います。サブクエリには WP_Query を使います(後述)。

query_postsが非推奨とされる理由

古いチュートリアルやレガシーなテーマでは、メインクエリのカスタマイズに query_posts() が使われていることがあります。

// 非推奨な書き方
query_posts( 'cat=1' );
if ( have_posts() ) {
    while ( have_posts() ) {
        the_post();
        // ...
    }
}

query_posts() には以下の問題があります。

  • メインクエリを直接上書きするため、ページネーションが壊れたり、他のテンプレート処理に影響が出る
  • 上書き前のクエリが失われるため、wp_reset_query() を呼び忘れると予期しないバグが起きる
  • WordPress公式ドキュメントでも「この関数は使うべきではない」と明記されている

既存コードで query_posts() を見かけたら、pre_get_posts への置き換えを検討してください。

pre_get_postsの仕組みと基本の書き方

pre_get_posts は、メインクエリがデータベースに問い合わせる直前にフックして、クエリの条件を書き換えるアクションフックです。メインクエリを上書きするのではなく、実行前に条件を差し替えるため、安全にカスタマイズできます。

functions.php に記述します。

function my_customize_main_query( $query ) {
    // 管理画面とメインクエリ以外には影響させない
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    // ここにカスタマイズ条件を書く
}
add_action( 'pre_get_posts', 'my_customize_main_query' );

最初の if 文は必ず入れてください。pre_get_posts は管理画面のクエリやサブクエリにも発火するため、この条件がないと意図しない箇所に影響が出ます。

実践的な使用例

アーカイブページの表示件数を変更する

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

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

カスタム投稿タイプをカスタムフィールドで並び替える

イベント一覧を開催日の昇順で表示する例です。

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

    if ( $query->is_post_type_archive( 'event' ) ) {
        $query->set( 'orderby', 'meta_value' );
        $query->set( 'meta_key', 'event_date' );
        $query->set( 'order', 'ASC' );
    }
}
add_action( 'pre_get_posts', 'my_customize_main_query' );

検索結果から固定ページを除外する

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

    if ( $query->is_search() ) {
        $query->set( 'post_type', 'post' );
    }
}
add_action( 'pre_get_posts', 'my_customize_main_query' );

特定カテゴリーをトップページの一覧から除外する

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

    if ( $query->is_home() ) {
        $query->set( 'category__not_in', [ 5 ] );
    }
}
add_action( 'pre_get_posts', 'my_customize_main_query' );

pre_get_postsとWP_Queryの使い分け

どちらもクエリを扱う仕組みですが、役割が明確に異なります。

pre_get_postsWP_Query
用途メインクエリの条件変更サブクエリの作成
記述場所functions.phpテンプレートファイル
対象メインループの出力内容メインループ以外の投稿取得

pre_get_postsを使う場面

  • アーカイブページの表示件数や並び順を変えたい
  • 検索結果の対象を絞りたい
  • トップページの投稿一覧の条件を変えたい

そのページが本来表示するデータの条件を変えたいとき

WP_Queryを使う場面

  • サイドバーに最新記事を出したい
  • 記事下に関連記事を表示したい
  • トップページに特定カテゴリーの記事を並べたい

メインの表示とは別に、追加で投稿を取得したいとき

// WP_Queryの例: 最新の関連記事を3件取得
$related = new WP_Query([
    'post_type'      => 'post',
    'posts_per_page' => 3,
    'category__in'   => [ $current_category_id ],
    'post__not_in'   => [ get_the_ID() ],
]);

if ( $related->have_posts() ) {
    while ( $related->have_posts() ) {
        $related->the_post();
        // 関連記事の出力処理
    }
    wp_reset_postdata();
}

WP_Query を使ったあとは wp_reset_postdata() を忘れずに呼んでください。メインクエリのグローバル変数($post など)を元に戻すために必要です。

よくあるミス

is_main_query() のチェック漏れ

pre_get_posts のコールバックで is_main_query() を確認していないと、ウィジェットやプラグインが内部で発行するサブクエリにまで影響が出ます。

is_home() と is_front_page() の混同

  • is_home(): 投稿一覧ページ(「表示設定」で指定した投稿ページ)
  • is_front_page(): サイトのフロントページ

固定ページをフロントページに設定している場合、is_home() はフロントページでは false になります。条件分岐を間違えると「条件が効かない」というハマりどころになります。

WordPress運用で
お困りではありませんか?

セキュリティ対策、パフォーマンス改善、緊急時の対応まで。 WordPress専門15年超のエンジニアが直接サポートします。

WordPress専門15年超 エンジニアが直接対応 月額20万円〜 最低契約6ヶ月