メインクエリを操作して投稿を表示させる pre_get_posts | KoMariCote

メインクエリを操作して投稿を表示させる pre_get_posts

WordPressのメインループで表示される一覧のカスタマイズができるコード、pre_get_posts についてのメモです。
トップページでは5件だけ表示して、カテゴリーページでは10件表示にする。検索結果には特定の投稿タイプのみ表示させたい。こういった要望があったときに使えます。

query_postsWP_Query などを使って表示数を指定する方法もありますが、このコードで記述した方が一ヶ所でどこの設定がどうなっているか確認しやすい(メンテナンスしやすい)ですし、パフォーマンス面でも良くなると思います。functions.php に書くのは悩ましい方もいると思いますが、少しずつコードを覚えて実装してみて下さい。

pre_get_posts とは

pre_get_posts はWordPressがアクセスしたURLをもとに、メインクエリで投稿を取得する前に処理を行いたいときに使うアクションフックというものです。

投稿した情報はデータベースに保存されており、投稿記事を表示させるには、データベースから表示させたい記事を指定しなければなりません。この時に「表示させたい記事の条件」をクエリと言います。

WordPressはアクセスしたURLによって自動的に表示させる投稿データを取得しています。
URLによって自動的に条件を決めている内容を「メインクエリ」、それ以外にこちらから指定する条件を「サブクエリ」と言います。

アクションフックは「とあるイベントが発生するときに実行する処理を追加するもの」です。WordPressのデフォルトの動きに変更を加えるために用意されています。
pre_get_posts は名前の通り「投稿を取得する前」というイベントを指しています。

add_action('pre_get_posts', 'my_preget_posts');
// このようなイメージ↓↓↓↓↓↓
add_action('投稿を取得する前に', 'my_preget_postsの処理を行う');

pre_get_posts の使い方 | 基本のコード

私はいつもこのようなコードを書いています。

function my_preget_posts($query) {
  if (is_admin() || ! $query->is_main_query()){
    return;
  }
  if ($query->is_home()) {
     $query->set('posts_per_page', 3);
     return;
  }
  if ($query->is_category()) {
    $query->set('order', 'DESC');
    $query->set('orderby', 'date');
  }
}
add_action('pre_get_posts', 'my_preget_posts');

まず一番始めにこのようなコードがあります。

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

これは管理画面を表示しているとき、もしくは現在のクエリがメインクエリでなければデフォルトの状態のまま返すという処理です。管理画面やメインクエリ以外ではクエリを変更したくないので、このコードは必ずつけます。

$query にはクエリ情報が入っていて、この中身を変更することでメインクエリを変更することができます。

基本の書き方は、if文でページを指定し、$query->set() で表示内容を設定します。他のページに影響が出ないよう条件分岐は必須です。

$query->set() は「クエリを→セットする」というイメージで、この部分を変更してカスタマイズしていきます。

if ($query->is_home()) { // どのページかを指定
 $query->set('パラメータ', パラメータに対応する値);
}

$query->set('posts_per_page', 3); であれば、「表示件数を最大3件にする」というコードです。

必要なクエリセットを書いたら add_action() を使って実行させます。

add_action('pre_get_posts', 'my_preget_posts');

add_action() は第一引数に「アクションフック名」、第二引数に「実行する関数名」を記述します。

この例では第一引数の pre_get_posts で「投稿を取得する前」を指定し、第二引数の my_preget_posts で「記述した内容を実行する」となります。

ここで使えるパラメータはたくさんあります。$WP_Query と同じパラメータを使用できるので、リファレンスや参考記事などで確認しながら、必要なものを設定してください。

よく設定する内容をいくつかここでご紹介します。

表示する最大件数を設定する | posts_per_page

最大表示件数を設定するパラメータは posts_per_page です。表示させたい最大表示件数を指定します。

if ($query->is_home()) {
   $query->set('posts_per_page', 3);
   // $query->set('posts_per_page', 最大表示件数)
   return;
}

これでトップページの投稿一覧は、最大3件を表示するようになります。表示件数を変えたい場合は最後の数字の部分を変更します。

表示順序を設定する | order, orderby

表示順序は、orderorderby パラメータを使用します。

  if ($query->is_category()) {
    $query->set('order', 'DESC');
    $query->set('orderby', 'modified');
  }

このコードでは表示順序を日付の降順(新しいものから順番)にしています。

order は順序(昇順・降順)、orderby は並び順をどの項目で決定するかを設定します。

順序はASCまたはDESC。
ASC(昇順)… 1 2 3 | A B C
DESC(降順)… 3 2 1 | C B A

日付での順序ならASCが古いものから表示、DESCは新しいものから表示されます。初期値がDESCなので、昇順にしたい場合のみASCを設定すれば大丈夫です。

タイトルやランダム、コメント数などたくさんの並び順で指定することができます。
【参照】Order & Orderby Parameters

カスタムフィールドの値から順序設定する

投稿の日付ではなく、カスタムフィールドで設定した値で順序設定したいときも対応できます。
例えば、イベントの一覧ページで開催日順に並べたいときに以下のような記述で並べ替えができます。

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

特定のカテゴリーを取得する

特定のカテゴリーのみ表示させたい場合、IDで取得する方法とカテゴリースラッグを指定する方法があります。

// カテゴリーIDを指定(IDが3のものを取得)
if ($query->is_home()) {
  $query->set('cat', 3);
}
// カテゴリーのスラッグを指定(カテゴリースラッグがuncategorizedを取得)
if ($query->is_home()) {
  $query->set('category_name', 'uncategorized');
}

特定のカテゴリー・タグを除外する | category__not_in, tag__not_in

カテゴリーの場合は category__not_in、タグの場合は tag__not_in を使います。

// 特定のカテゴリー除外
if ($query->is_home()) {
  $query->set('category__not_in', array(3, 5));
}
// 一つの場合でもarrayを使う
if ($query->is_home()) {
  $query->set('category__not_in', array(5));
}
// 特定のタグ除外
if ($query->is_home()) {
  $query->set('tag__not_in', array(3, 5));

値は配列で渡さなければならないため、設定値が一つしかなくても array() を使います。

カテゴリーの場合はマイナス記法というものも使えますが、タグには使えません。

// カテゴリーのみ、値に「-」をつけて除外にする方法がある
if ($query->is_home()) {
  $query->set('cat', '-3,-5');
}

特定のタクソノミーを除外する

タクソノミーの場合は、他のものより設定が多くなります。

if ($query->is_home()) {
  $tax_query = array(
    array(
      'taxonomy'  => 'tax_name',
      'terms'     => array(5, 6),
      'operator'  => 'NOT IN',
    ),
  );
  $query->set('tax_query', $tax_query);
}

特定の投稿タイプを指定する

例えば検索ページで特定の投稿タイプのみを表示させるようにしたい場合、 post_type を使用して投稿タイプ名を指定します。通常の投稿なら 'post'、固定ページは 'page'、カスタム投稿なら設定した投稿タイプ名を設定します。

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

配列にすることで複数追加することができます。

if ($query->is_search()) {
  $query->set('post_type', array('post', 'custom_post'));
}

スマホとPCで表示方法を変更する

PCでは数を多く表示させてスマホでは数を減らしたいなど、スマホとPCで表示方法を変更したい時は wp_is_mobile() といったデバイスサイズを条件分岐に指定して対応できます。

if ($query->is_home() && wp_is_mobile()) {
  $query->set('posts_per_page', 4);
}

【PR】

pre_get_posts の使用注意点

pre_get_posts は固定ページを指定したり、functions.php以外のテンプレートファイルに記載しても動作しないので覚えておきましょう。

固定ページでは使えない

「固定ページ」で pre_get_posts は使えません。

pre_get_posts は単一の固定ページのリクエスト(ページテンプレート)に対するクエリを変更するのに用いるべきではありません。なぜなら ‘is_page’、’is_singular’、’pagename’ および他のプロパティ(pretty パーマリンクを使っているかどうかによる)が parse_query() メソッドによってセットされた後だからです。詳しくは クエリ概要 を見てください。
これと同様に、pre_get_posts はテンプレートファイル(例えば archive.php)内では動作しません。なぜならクエリが完了した後だからです。
これらの設定に精通していて、かつ設定を調整しようと望むのでなければ、ページテンプレート内で new WP_Query を使って単一の固定ページのリクエスト(ページテンプレート)に対するクエリを変更するのを推奨します。

プラグイン API/アクションフック一覧/pre get posts – WordPress Codex 日本語版

固定ページで投稿情報を取得したい場合は、「WP_Query」や「get_posts」を使用します。

テンプレートファイルに書いても動作しない

テンプレートファイルは index.php や archive.php などのファイルです。このテンプレートファイルは読み込まれた時点でメインクエリが読み込まれています。
pre_get_posts が動作するメインクエリで投稿を取得する前を過ぎてしまっているため、動作しません。functions.php に記載するようにしましょう。

カテゴリー : WordPress

タグ :

index