pre_get_posts でアーカイブ(一覧)ページの表示を工夫する

WordPressのロゴ。

標準的な WordPpress とテーマのセットでは、アーカイブページなどに用いられる have_posts() 関数は、投稿ページを規定のページ数だけ取得するために利用されます。 しかしながら、例えば"特定のページを表示したくない場合" や、"投稿ページと固定ページの両方の新着が欲しい" 場合には、カスタマイズが必要です。 ここでは pre_get_posts アクションをフックして、アーカイブ(一覧)ページの表示方法を工夫します。

pre_get_posts アクションとは

pre_get_posts アクションを簡単に説明すると、post (投稿) を取得する前に実行されるアクションです。 したがって、投稿を取得するよりも前に、取得する条件などを調整(変更)すれば、実際に取得するタイミングで条件に見合った結果が得られる、ということです。 ここで調整するパラメータは クエリ (query) です。

クエリ(query)とは

クエリを簡単に説明すると、データベースやサーバに問い合わせるときのパラメータです。 最も身近に見られる例は URL です。例えば WordPress で表示されるアドレスの末尾の方に &= が見つかります。 それがクエリの正体です。あるアドレス A に対し、B と C というパラメータを与えてページを表示したい、というときは、A?B=value&C=value となります。 詳細を理解していなくても pre_get_posts アクションを利用することは可能です。

functions.php への追記

pre_get_posts アクションフックを利用して処理を実行するために、例えば次のようなコードを functions.php に追記します。 このコードはトップページ(home, index.php または home.php など)の表示の際に、pre_get_posts にフックして、そのクエリを調整します。

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

    if ( $query->is_home() )
    {
        $query->set( 'posts_per_page', '5' );
        $query->set( 'post_type', array('post', 'page') ); 
       //to exclude undefined category.
        $query->set( 'category_not_in', '1');
        $query->set( 'cat', '-1' );//same as category_not_in
    }
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

先にこのコードについて簡単に説明します。このコードは次のような処理を実行します。

クエリ、つまりページを一覧して欲しいという命令が、is_home(トップページに対するもの)だった場合、 カテゴリID が 1 (一般的には"未分類")の記事を除く、投稿(post) あるいは 固定ページ(page) を最大 5ページ取得する。

したがってこのコードはカテゴリ別アーカイブやタグ別アーカイブには効果がない、ということです。 また、WordPress の "表示設定" などで、最大表示数が 10 と設定されている場合などであっても、 トップページには 最大 5 件の記事しか表示されない、ということでもあります。

作り自体はシンプルなのでコード自体を読んでしまえば大よそどのようなことができるか理解できますが、以降にもう少しだけ詳細な解説を続けます。

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

!query->ismain_query() にあるように、先のコードでは pre_get_posts に与えられるクエリがメインクエリかどうかを確認しています。 まずメインクエリサブクエリについて解説しておきます。メインクエリはページを表示するために必要になる、主要なクエリです。 これを変更してしまうと期待したページが表示されなくなる、とうことです。ではサブクエリは、というと、 メインクエリによって取得されるページに対して、さらに追加されたクエリです。

例えば、トップページを表示したい、という要求を満たすものがクエリがメインクエリであり、 そのトップページの中で、さらに新着ページを取得したい、という要求を満たすものがサブクエリです。

つまりここではメインクエリの場合には処理を行わず、サブクエリの場合にのみ処理を続行する、というコードを扱うことになります。

クエリの調整

取得したいページの条件を設定するには、クエリのパラメータを設定します。 WordPress で扱うことができるクエリのパラメータは別途調べてもらうとして、この例で示した 3 つのコードについて解説します。

set( 'posts_per_page', '5' ); は、取得する投稿の上限を 5 件までとする設定です。 set( 'post_type', array('page', 'post') ); は、取得する投稿の種類が固定ページまたは投稿ページであることを要求します。 複数の値を設定する場合は、array を利用することになります。

最後に set( 'category_not_in', '1'); ですが、これは取得する投稿のカテゴリのカテゴリID が、1 ではないことを要求します。 ここで一般的には、カテゴリID 1 は "未分類" です。ただし、このクエリ自体が上手く機能しないことがあるようです。 そういう場合には、$query->set( 'cat', '-1' ); のように、cat パラメータを設定します。 これで問題を回避できない場合には、何か異なる個所に原因を持っている可能性があります。

"未分類" を完全に識別できない問題

固定ページにカテゴリを設定することができるようにするとき、 ある固定ページにカテゴリが設定されないときは、そのカテゴリは表面上 "未分類" に見えます。 この空白による未分類は、カテゴリ設定されたページの"未分類"とは異なる点に注意が必要です。

クエリによってカテゴリが設定されていないページを取得する、という操作はできそうにないため、 カテゴリが設定されていない "未分類" の固定ページを一覧する、一覧から除くことができません。

例えばコンタクトやプライバシーポリシーなどのシステムライクなページをカテゴリなしの固定ページにしているとき、 新しく類似する固定ページを追加すると、新着一覧にその業務チックなページが表示されてしまいます。 もう少し例を具体化すると、プライバシーポリシーのページを新規に追加したとき、新着一覧ページにそのページが掲載されてしまうのです。 確かに新着ではありませんが、表示したくないものです。

この問題を解決するにはシステムライクなカテゴリ専用のカテゴリを設けることです。 そして対象のカテゴリ ID を示すページを取り除きます。 最も簡単な方法は、"未分類" のカテゴリをシステムライクなカテゴリ以外に設定しないことです。 するとカテゴリID = 1 となるページを除く仕組みがあらゆる個所で利用できます。