【WordPress】pre_get_postsの使い方

【WordPress】pre_get_postsの使い方

pre_get_postsって?

WordPressのメインクエリをカスタマイズできるアクションフックです。
WordPressにはメインクエリとサブクエリがありますが、そのうちのメインクエリを生成する直前に呼び出される関数です。これを使うとメインクエリの内容を変えられます。

プラグイン API/アクションフック一覧/pre get posts

https://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7/pre_get_posts

メインクエリ

WordPressはデフォルトでリクエストされたURLに応じてページ情報をデータベースから取り出します。例えばこのサイトのABOUTページhttps://notes.sharesl.net/about/の場合は固定ページでスラッグが「about」のページ情報をデータベースから取り出しています。
この最初にデータベースから取得したページ情報を「メインクエリ」といいます。その上で、この例で言えばpage.phppage-about.phpといった固定ページ用のテンプレートに反映されます。テーマ作成者はそれぞれのメインクエリに応じたテンプレートを使い分けることで簡単にテーマが作成できるような仕組みになっています。

サブクエリ

WP_Queryなどを使ってメインクエリとは別の情報を取得する場合、その取得したページ情報は「サブクエリ」と呼ばれます。例えば、固定ページにWP_Queryget_postsなどを使って新着リスト表示させる時などは、固定ページのページ情報がメインクエリ、新着リストはサブクエリとなります。

そのためサブクエリはメインクエリの後にデータ取得するので、「固定ページで新着リストを表示する」というページを作るとするとデータベースには2回問い合わせることになります。

これは素晴らしい機能で、WordPressテーマを構築する上で欠かせないものです。

じゃあ何が問題かといいますと、

例えば初心者の時によくあったのが、カテゴリーページやアーカイブページで思った通りのリストが表示されないのでWP_Queryで一覧をカスタマイズしてしまうようなことです。アーカイブページにはそもそも「メインクエリ」がすでに呼ばれていますが、件数の表示やカスタムフィールドで条件を絞ったりするなどをどこで設定して良いかわからず、メインクエリを無視してサブクエリで新たにデータを取得してそれを一覧として使ってしまっていました。
そうすると同じような内容のデータを2回取得しにいくわけなのでかなりの無駄が発生し、ページが重くなります。
さらにページネーションを使っていた場合はもっと最悪です。そもそもページネーション処理自体がかなり重くなる処理なのですが、それをサブループでpagedなどのオプションで作ってしまうと2ページ目や3ページ目が非力なサーバーだと重すぎて落ちたりします。

こういったことをpre_get_postsアクションフックを使うと解決できます!
メインクエリを取得する直前に取得するクエリをカスタマイズできるので、こういった無駄なデータ取得処理を減らすことができます。

pre_get_postsの使い方

一覧記事の表示数はデフォルトを20件にしていますが、カテゴリーページの記事数を12件に変更してみます。functions.phpに以下を追記します。

/*-------------------------------------------*/
/* メインクエリの書き換え(各ページのループ)
/*-------------------------------------------*/
function custom_query($query)
{
 //管理画面・プレビュー表示などの回避
  if (is_admin() || ! $query->is_main_query()) {
    return;
  }

  //カテゴリーページ
  if ($query->is_category()) {
    $query->set('posts_per_page', 12);
    $query->set('orderby', 'date');
    $query->set('order', 'DESC');
    return;
  }

}
add_action('pre_get_posts', 'custom_query');

これでメインクエリをカスタマイズしてほとんどすべての一覧ページの記事数を12件にできました。めちゃくちゃ簡単ですね。これでわざわざカテゴリーページだけを12件に変えるためにWP_Queryを使わなくていいわけです。

ここまでの説明はけっこうググるといろんなサイトに書いてあるのですが、初心者の「あくしょんふっくってなに?」状態の時は、この後実際にカテゴリーテンプレートには何を書けばいいのかわからなくて小一時間迷ったことがあります。笑

そのため一応カテゴリーテンプレートでのループの書き方も書いておきます。

if ( have_posts() ) :
 while ( have_posts() ) :
    the_post();

    //ループの中身

  endwhile;
endif;

以上!これだけです。あの時は何を迷ってたんやろ・・・笑

pre_get_postsでの条件分岐

ページごとの条件分岐はテンプレートタグと同じなので、テーマを一度作ったことがあるならなんなく理解できます。

//TOPページ
$query->is_front_page()
$query->is_home()

//投稿ページ 
$query->is_single()

//固定ページ
$query->is_page()

//カテゴリーページ
$query->is_category()

//タグページ
$query->is_tag()

//アーカイブページ 
$query->is_archive()

//カスタム投稿タイプアーカイブページ 
$query->is_post_type_archive( '投稿タイプ' )

//日付アーカイブページ
$query->is_date()

//月別アーカイブページ 
$query->is_month()

//年別アーカイブページ 
$query->is_year()

//投稿者アーカイブページ
$query->is_author()

//タクソノミーページ 
$query->is_tax()

//検索結果ページ 
$query->is_search()

//RSSフィードページ
$query->is_feed()

//404ページ 
$query->is_404()

大体使うのはこの辺りかと思います。

pre_get_postsを使う際の注意点

固定ページでは使えない

固定ページのメインクエリにカスタム投稿タイプを当てたりできるのかな?と思って、以下のようなコードを書いてみました。

if ($query->is_page()) {
  //newsカスタム投稿タイプを表示させる
  $query->set('post_type', 'news');
  $query->set('posts_per_page', 10);
  $query->set('orderby', 'date');
  $query->set('order', 'DESC');
  return;
}

結果、これはできません。

具体的には公式のリファレンスに以下のような記載があります。

pre_get_posts
 は単一の固定ページのリクエスト(ページテンプレート)に対するクエリを変更するのに用いるべきではありません。なぜなら 'is_page'、'is_singular'、'pagename' および他のプロパティ(pretty パーマリンクを使っているかどうかによる)が parse_query() メソッドによってセットされた後だからです。詳しくは クエリ概要 を見てください。

プラグイン API/アクションフック一覧/pre get posts

固定ページや投稿ページでこのようなカスタマイズは不可能みたいです。カスタム投稿アーカイブページを使いましょう。

テンプレートファイルでは動作しない

テンプレートファイルにpre_get_postsを書いても動作しません。すでにメインクエリが出来上がったあとにテンプレートファイルを呼ぶ仕組みなので当然っちゃ当然ですが注意です。アクションフックや関数は必ずfunctions.phpに書くようにしましょう。

query_postsはダメなの?

query_postsはメインクエリを置き換える関数として存在しますが、非推奨のため使用してはいけません。公式リファレンスに以下の記載があります。

この関数はプラグインまたはテーマの中で使われることを想定されていません。後ほど出てくる説明にあるように、メインクエリーを変更するにはパフォーマンス面でも優れたより良いオプションが存在します。

テンプレートタグ/query posts

この後の説明で「パフォーマンス面でも優れたより良いオプション」としてpre_get_posts強く推奨すると書かれて紹介されているので、公式の見解としてもquery_postsは完全にアウトです。使うとパフォーマンスが悪くなり良いことは何もないので使用しないようにしましょう。

まとめ

WordPressのパフォーマンスを改善したいならpre_get_postsを使いましょう!
特に規模の大きなサイトでは影響が大きくなります。WP_Queryで頑張るより見通しもよくなるし管理も簡単ですのでまだ使ったことがない場合は試してみてください!