WordPress の投稿一覧ページで表示件数を変更するときは pre_get_posts を使う

ワードプレス ロゴ

先日、WordPress で投稿者アーカイブなど不要なページを 404 にする記事を書いたときに pre_get_posts が出てきたので、自分の復習も兼て pre_get_posts について簡単にまとめてみた。

まず基本の使い方

例えば、トップページの表示件数は6件、カテゴリーページの表示件数10件とかにする場合は functions.php に次のようにして pre_get_posts のアクションフックを使う

functions custom_pre_get_posts( $query ) {
    if ( is_admin() )
        return;

    if ( $query->is_home() && $query->is_main_query() ) {
         $query->set( 'posts_per_page', 6 );
         return;
    }

    if ( $query->is_category() && $query->is_main_query() ) {
         $query->set( 'posts_per_page', 10 );
         return;
    }
}
add_action('pre_get_posts', 'custom_pre_get_posts');

とする。ちょっと基本的なことから解説すると、2箇所出てくる custom_pre_get_posts というのは自分で好きにつけてよい(もちろん2箇所とも同じにしなければならない)。

それで最初の

    if ( is_admin() )
        return;

は管理画面に影響を及ばさないための対処。WordPress の表側だけで色々な設定していきますよ、ということ。

それで if ( $query->is_category() && $query->is_main_query() ) の $query->is_main_query() が重要。これでメインクエリに対して、$query->set( ‘posts_per_page’, 10 ) ってのが適用される。

メインクエリって何? って感じだけど、カテゴリーページなら記事一覧の記事データをデータベースから取得してくる条件みたいなものだ。

要は index.php とか category.php とかに

<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
	<article>
		<h2><?php the_title(); ?></h2>
		<?php the_excerpt(); ?>
	</article>
<?php endwhile; ?>
<?php endif; ?>

なんてあるループのところに上記の設定 $query->set( ‘posts_per_page’, 6 ) とかを適用しますよってことになる。

逆に if 条件に $query->is_main_query() をつけないとメインじゃないループ、たとえばサイドバーとかの新着記事とかに影響を及ぼすことがある。

ほかには特定のカテゴリーを除外したり

上記の例は表示する記事件数を指定する例だったけど、トップページの新着記事にはあるカテゴリーの記事は表示させたくない場合。

たとえばカテゴリーIDが 3 と 5 のカテゴリーの投稿を表示させたくない場合は

functions custom_pre_get_posts( $query ) {
    if ( is_admin() )
        return;

    if ( $query->is_home() && $query->is_main_query() ) {
         $query->set( 'category__not_in', array( 3, 5 ) );
         return;
    }

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

なんてすればよい。

それとか、特定の投稿を除外したい場合、たとえば投稿 ID が 20 と 51 の投稿を除外したい場合は

functions custom_pre_get_posts( $query ) {
    if ( is_admin() )
        return;

    if ( $query->is_home() && $query->is_main_query() ) {
         $query->set( 'post__not_in', array( 20, 51 ) );
         return;
    }

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

とする。

複数のループを入れたいんだけど、っていう場合

上みたいにやれば記事一覧のループに対していろいろ指定できるのはわかったけど、それとは別のループを入れたいんだけど、という場合。たとえば

新着記事
・新着記事1
・新着記事2
・新着記事3

「ほげ」カテゴリーの新着
・ほげ記事1
・ほげ記事2
・ほげ記事3

ってしたい場合。新着記事のほうは

<?php if ( have_posts() ) : ?>
<h2>最新記事</h2>
<?php while ( have_posts() ) : the_post(); ?>
	<article>
		<h3><?php the_title(); ?></h3>
		<?php the_excerpt(); ?>
	</article>
<?php endwhile; ?>
<?php endif; ?>

とメインのループで表示させ、表示させる件数などは上のように pre_get_posts のアクションフックで対応すればよい。

対して「ほげ」カテゴリーの新着のほうは、メインクエリとは別のクエリを生成してやる。

そのためには new WP_Query を使う。「ほげ」カテゴリーのスラッグが hoge なら

$args = array(
    'posts_per_page' => 3,
    'category_name'  => 'hoge',
);

$the_query = new WP_Query( $args );

<?php if ( $the_query->have_posts() ) : ?>
<h2>「ほげ」カテゴリーの新着</h2>
<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
	<article>
		<h2><?php the_title(); ?></h2>
		<?php the_excerpt(); ?>
	</article>
<?php endwhile; wp_reset_postdata(); ?>
<?php endif; ?>

としてやれば、「ほげ」カテゴリーの新着記事が 3 件表示される。

$the_query = new WP_Query( $args );

の一行で、メインクエリとは別のクエリを生成している。そのクエリの諸条件を $args の配列で指定しているわけど。

ループのほうのポイントはメインループのほうでは単に have_posts() とかなっているのが $the_query->have_posts() とかなっているところ。

これで $the_query = new WP_Query( $args ) で生成したクエリのことを扱うことができる。($the_query は好きに決めれて $my_query とか $query とか、さらにいくつか使用する場合は、 $the_query01、 $the_query02…とかでオッケー)

さらに重要なのが endwhile; の後の wp_reset_postdata(); 。これは new WP_Query を使ったクエリを実行した後で、もとメインクエリに戻すために必要。

これが無いと、これ以降 new WP_Query を使ったクエリの影響が続いてしまう。

それで他にどんな条件を指定できるの?

表示させる件数や特定の記事の除外とかを上で紹介したけど、他にも色々使える。あんまりにも色々使えるので下記を参照してください。

WordPress Codex 日本語版 「関数リファレンス/WP Query」

のページにある「~パラメータ」で紹介されているのが使えます。このページでは new WP_Query で使う例が紹介されているけど、たとえば

$args = array(
	'date_query' => array(
		array(
			'year'  => 2012,
			'month' => 12,
			'day'   => 12,
		),
	),
);
$query = new WP_Query( $args );

という2012年12月12日の投稿を取ってくるという例の場合。それをメインクエリを変更するpre_get_posts のアクションフック内で使うのであれば

if ( $query->is_home() && $query->is_main_query() ) {
    $query->set( 'date_query', array(
        array(
	    'year'  => 2012,
	    'month' => 12,
            'day'   => 12,
        ),
     ) );
}

と、とにかく $query->set( ‘……’, ‘……’ ) の形にすればよい。

とざっと pre_get_posts のアクションフックと、メインのループとは別のループを使う場合についてまとめてみました。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です