WordPressでサイトを構築する際、特定のカテゴリーの記事一覧やカスタム投稿タイプのリストを任意の位置に表示したい場面は非常に多いです。
このようなケースでは、メインクエリとは別に「サブループ」を作成して記事を取得します。
しかし、サブループで記事を表示した際に多くの開発者が直面するのが、ページネーション(ページ送り)が正常に動作しないという問題です。
メインクエリであれば標準機能で自動的に処理されますが、サブループの場合は適切な設定を手動で行う必要があります。
本記事では、WordPressのサブループでページネーションを正しく実装するための具体的な手順とコードの記述方法について詳しく紹介します。
サブループでページネーションが失敗する理由
WordPressの標準的なページネーション機能は、基本的にグローバル変数である$wp_queryの状態を参照しています。
メインループはこのグローバル変数を使用するため、特に追加の設定をしなくてもページ番号を認識し、適切な記事を表示できます。
一方で、WP_Queryを使用して独自に作成したサブループは、メインクエリとは独立したインスタンスとして動作します。
そのため、ページネーション用の関数をそのまま呼び出すだけでは、現在何ページ目を表示すべきか、あるいは全何ページ存在するのかという情報を正しく受け取ることができません。
この情報の不一致が原因となり、2ページ目以降をクリックしても「404エラー(ページが見つかりません)」が発生したり、常に1ページ目の内容が表示されたりするトラブルが発生します。
実装の全体像と必要なステップ
サブループにページネーションを実装するには、大きく分けて3つのステップが必要です。
第一に、現在のページ番号を取得し、それをWP_Queryの引数に渡す必要があります。
第二に、サブループで取得した記事データに基づいたページネーションのリンク(HTML)を生成することです。
第三に、メインクエリの干渉を避けるための適切なデータリセットを行うことが重要です。
これらの一連の流れを正しく記述することで、カスタム投稿や特定の条件で抽出したリストでもスムーズなページ送りが可能になります。
ステップ1:現在ページ番号の取得
まずは、現在ユーザーがどのページに滞在しているかをプログラムに教える必要があります。
WordPressでは、ページ番号を保持する変数はページのテンプレートタイプによって異なるため注意が必要です。
通常の投稿アーカイブやカテゴリーページではpagedというクエリ変数が使われますが、静的なフロントページ(トップページ)として設定されたページではpageという変数が使われることがあります。
以下のコードを使用することで、どのようなページ構成であっても確実に現在のページ番号を取得できます。
// 現在のページ番号を取得する
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
if (is_front_page()) {
$paged = (get_query_var('page')) ? get_query_var('page') : 1;
}
このコードでは、まずget_query_var('paged')を試し、取得できない場合は1ページ目とみなします。
さらにis_front_page()の条件分岐を加えることで、フロントページでの挙動もカバーしています。
このように「paged」と「page」の両方を考慮することが、エラーを防ぐための第一歩となります。
ステップ2:WP_Queryへの引数設定
次に、取得したページ番号をWP_Queryに渡して、正しい範囲の記事を抽出させます。
ここで重要になるのがpagedパラメータとposts_per_pageパラメータです。
posts_per_pageは1ページあたりに表示する記事数を指定し、pagedは現在のページがどこであるかを指定します。
// WP_Queryの引数設定
$args = array(
'post_type' => 'post', // 投稿タイプを指定
'posts_per_page' => 5, // 1ページに表示する件数
'paged' => $paged, // 先ほど取得した現在のページ番号
);
// クエリの実行
$the_query = new WP_Query($args);
この設定により、例えば2ページ目を表示している場合、WordPressは「5件目から10件目の記事」を自動的に計算して取得してくれます。
pagedを省略してしまうと、常に最新の1ページ目が表示されてしまうため、必ず指定するようにしましょう。
ステップ3:ループの記述と表示
準備したクエリを実行し、通常のループ処理を記述します。
この際、have_posts()やthe_post()は、必ずインスタンス化したオブジェクト(ここでは$the_query)から呼び出すようにしてください。
if ($the_query->have_posts()) :
while ($the_query->have_posts()) : $the_query->the_post();
// 記事のタイトルなどを出力
the_title('<h2>', '</h2>');
endwhile;
else :
echo '<p>記事が見つかりませんでした。</p>';
endif;
ここではまだページネーションのリンクは出力されませんが、まずは記事が正しく抽出されているかを確認する段階です。
ステップ4:ページネーションリンクの生成
記事一覧の直後に、ページを切り替えるためのボタン(ページネーション)を出力します。
サブループでページネーションリンクを作る場合、最も便利な関数がpaginate_linksです。
この関数の引数に、サブループの最大ページ数(max_num_pages)を渡すことが成功の鍵となります。
// ページネーションの出力
echo paginate_links(array(
'total' => $the_query->max_num_pages, // 全ページ数を指定
'current' => $paged, // 現在のページを指定
'format' => '?paged=%#%', // リンクのURL形式
));
// 投稿データのリセット
wp_reset_postdata();
totalパラメータにサブループ独自の最大ページ数を指定しないと、WordPressはメインクエリのページ数を参照してしまい、表示が崩れたりリンクが機能しなくなったりします。
また、ループが終わった直後には必ずwp_reset_postdata()を呼び出してください。
これを忘れると、その後のページ構成やメインクエリを参照する関数が正常に動作しなくなる恐れがあります。
実装の完全なサンプルコード
ここまでの手順を一つのコードブロックにまとめると、以下のようになります。
このコードをテンプレートファイルにコピーし、必要に応じて条件を書き換えて使用してください。
<?php
// 1. 現在のページ番号を取得
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
if (is_front_page()) {
$paged = (get_query_var('page')) ? get_query_var('page') : 1;
}
// 2. WP_Queryの引数を設定
$args = array(
'post_type' => 'news', // カスタム投稿タイプ「news」を指定
'posts_per_page' => 10, // 10件ずつ表示
'paged' => $paged, // ページ番号を反映
);
// 3. クエリの実行
$the_query = new WP_Query($args);
?>
<div class="post-list">
<?php if ($the_query->have_posts()) : ?>
<?php while ($the_query->have_posts()) : $the_query->the_post(); ?>
<article>
<h3><?php the_title(); ?></h3>
<div class="content">
<?php the_excerpt(); ?>
</div>
</article>
<?php endwhile; ?>
<!-- 4. ページネーションリンク -->
<div class="pagination">
<?php
echo paginate_links(array(
'total' => $the_query->max_num_pages,
'current' => $paged,
'mid_size' => 2,
'prev_text' => '前へ',
'next_text' => '次へ',
'type' => 'list', // <ul><li>形式で出力
));
?>
</div>
<?php else : ?>
<p>該当する記事がありません。</p>
<?php endif; ?>
</div>
<?php
// 5. グローバル変数のリセット
wp_reset_postdata();
?>
実行結果として出力されるHTML構造は以下のようになります。
<div class="post-list">
<article>
<h3>新製品のお知らせ</h3>
...
</article>
<!-- (記事が続く) -->
<div class="pagination">
<ul class="page-numbers">
<li><span class="page-numbers current">1</span></li>
<li><a class="page-numbers" href="?paged=2">2</a></li>
<li><a class="page-numbers" href="?paged=3">3</a></li>
<li><a class="next page-numbers" href="?paged=2">次へ</a></li>
</ul>
</div>
</div>
よくあるトラブルと解決策
実装を進める中で、どうしても解決できない不具合が発生することがあります。
特に多いのが、「2ページ目をクリックすると404エラーになる」という現象です。
これはサブループの設定ミスではなく、WordPressのメインクエリ側の判定が影響している場合が多いです。
管理画面の設定を確認する
WordPressの管理画面から「設定 > 表示設定」にある「1ページに表示する最大投稿数」を確認してください。
もしサブループのposts_per_pageで指定した数値よりも、この管理画面の設定値が「大きい」場合、不具合が生じることがあります。
例えば、管理画面で「10件」と設定されているのに、サブループで「1件」ずつ表示させようとすると、WordPressは全体の記事件数が足りないと誤認し、2ページ目が存在しないと判断して404を返すことがあります。
これを防ぐためには、管理画面の設定値をサブループの件数よりも小さくするか、pre_get_postsアクションフックを使用してメインクエリを調整する手法が有効です。
URLの構造に注意する
パーマリンク設定を「基本」以外にしている場合、paginate_linksのformat引数を正しく調整する必要があります。
通常はWordPressが自動で判断してくれますが、固定ページ内で複数のサブループを回す場合などは、URLが重複しないよう配慮が必要です。
基本的には、本記事で紹介した標準的なpaginate_linksの使用法で多くのケースはカバーできます。
応用:複数のサブループでページネーションを使う
一つのページ内に異なるカテゴリーのリストを複数設置し、それぞれに個別のページネーションを付けたいという要望もあります。
しかし、標準のpagedというパラメータは一つしか使えないため、単純な実装では全てのリストが同時にめくられてしまいます。
これを回避するには、URLパラメータ名をユニークなものに変更するという高度なカスタマイズが必要です。
例えば、一つ目のリストには?paged1=2、二つ目のリストには?paged2=3という変数を割り当てるようにプログラムを組みます。
ただし、この方法はSEOやURLの複雑化を招くため、可能な限り1ページにつき1つのメインとなるページネーションを設計することをおすすめします。
まとめ
WordPressのサブループにおけるページネーション実装は、一見難しく見えますが、構造を理解すればシンプルです。
重要なのは、現在のページ番号を正確に取得すること、そしてWP_Queryのmax_num_pagesをページネーション関数にしっかりと受け渡すことの2点に集約されます。
特に「ページ番号の取得(pageとpagedの使い分け)」と「ループ後のリセット」を忘れずに行うことが、バグのないサイト制作に繋がります。
今回紹介した基本のコードをベースに、デザインに合わせてHTML構造や表示件数を自由にカスタマイズしてみてください。
正しく実装されたページネーションは、ユーザーの利便性を大きく向上させ、サイトの滞在時間改善にも寄与する重要な要素となります。
