タイトル

【WordPress】先頭に固定表示 + 1ページあたりの表示数を変えない【コードコピペでOK】


こんにちは。マークアップエンジニアのヤナダ(@yanada_front)です。

この note では、WordPress において、
「ブログの先頭に固定表示」機能 を利用しつつ、
  1ページあたりの「記事の表示数」を一定に保つ
ための WpQuery を公開しています。

1. 前提

下記が、対象読者の一例です。

・1ページあたり、9個の投稿を表示したい
・「固定表示」を設定した投稿は、1ページ目のトップに固定したい
・3投稿「固定」した場合、固定3 + 通常9 =12記事 ではなく、全体で9記事になるように設定したい

・他のサイトのやり方を試したが、うまくいかない
2ページ目以降がズレてしまう

画像1

一見カンタンそうですが、意外と小手先の対応では実現できません。
私の考える限り、4つの WpQuery と 4つの条件分岐 を組み合わせる必要があります。一々考えるのは面倒だと思うので、コピペで使えるようにソースを公開します。

【注意】実務において一番重要なのは、クライアントに「トップ固定機能を使う予定があるか?」「固定したときに、1ページあたりの表示件数が変わっても問題ないか?」と確認することです。使う予定がないのなら、下記の煩雑なソースを使う必要はありません。


2. コード(コピペでOK)

下記を適宜、書き換えて下さい。
 ・カテゴリ → hogehoge
 ・表示したい「投稿」の数 → 9
 ・繰り返し 表示(include)するソース → category-hogehoge_temp.php
 ・投稿が0件のとき表示するソース → content-none.php

<section class="category_hogehoge_mainbox">
<?php
// 【1ページ目・2ページ目以降 共通クエリ 2種】
//  (Q1) ページャー用クエリ
$args = array(
  'post_type' => 'post',
  'category_name' => 'hogehoge',
  'posts_per_page' => 9,  // 1ページに表示する「投稿」の数
  'paged' => get_query_var('paged')
);
$pager_query = new WP_Query($args);
//  (Q2) 固定ページ抽出クエリ
$sticky = get_option('sticky_posts');
$args = array(
  'post_type' => 'post',
  'category_name' => 'hogehoge',
  'posts_per_page' => -1,  // 固定ページはすべて表示
  'post__in' => $sticky
);
$sticky_query = new WP_Query($args);
// 固定ページ総数を $sticky_number に代入
if ($sticky[0] == true) {
  $sticky_number = $sticky_query->found_posts;
} else {
  $sticky_number = 0;
}
// 1ページ目に表示する、通常ページの本数を求める。
// (※変数 $normal_number は、2ページ目以降の offset にも利用する。)
$normal_number = 0;
if (9 - $sticky_number > 0) {
  $normal_number = 9 - $sticky_number;
}
?>
<?php
//【i. ページが1件以上存在する場合】
if ($pager_query->have_posts() == true) : ?>
  <?php
  // 【i-A. 1ページ目の場合】
  if (get_query_var('paged') == 0 || get_query_var('paged') == 1) :
    //【i-A-1. 固定ページが存在する場合】
    // 固定投稿 + 通常投稿(本数調整)→ 合計9本
    // (Q2) 固定ページ抽出クエリ + (Q3-1) 通常ページ抽出クエリ(paged = 1) で表示。
    //
    //【i-A-2. 固定ページが1つもない場合】
    // 通常投稿 → 9本
    // (Q3-1) 通常ページ抽出クエリ(paged = 1) のみで表示。
    //
    // ---------
    //
    // (Q3-1) 通常ページ抽出クエリ(paged = 1)
    $args = array(
      'post_type' => 'post',
      'category_name' => 'hogehoge',
      'posts_per_page' => $normal_number,
      'post__not_in' => get_option('sticky_posts'), /* 先頭固定は除外 */
      'paged' => 1
    );
    $normal_query_paged1 = new WP_Query($args);
  ?>
    <?php if ($sticky[0] == true) :
      //【A-1. 固定ページが存在する場合】
      // (Q2) 固定ページ抽出クエリ を出力
    ?>
      <?php while ($sticky_query->have_posts()) : $sticky_query->the_post(); ?>
        <?php include("category-hogehoge_temp.php"); ?>
      <?php endwhile; ?>
    <?php endif; ?>
    <?php
    //【A-1,A-2 共通】
    // (Q3-1) 通常ページ抽出クエリ(paged = 1) を出力
    while ($normal_query_paged1->have_posts()) : $normal_query_paged1->the_post(); ?>
      <?php include("category-hogehoge_temp.php"); ?>
    <?php endwhile; ?>
  <?php
  //【i-B. 2ページ目以降の場合】
  else :
    // (Q3-2) 通常ページ抽出クエリ(paged > 1)
    // offset を調整
    $args = array(
      'post_type' => 'post',
      'category_name' => 'hogehoge',
      'posts_per_page' => 9,
      'post__not_in' => $sticky, /* 先頭固定は除外 */
      'offset' => (get_query_var('paged') - 2) * 9 + $normal_number
    );
    $normal_query_paged2after = new WP_Query($args);
  ?>
    <?php while ($normal_query_paged2after->have_posts()) : $normal_query_paged2after->the_post(); ?>
      <?php include("category-hogehoge_temp.php"); ?>
    <?php endwhile; ?>
  <?php endif; ?>

<?php
//【ii. ページが1件も存在しない場合】
else : ?>
  <?php include("content-none.php"); ?>
<? endif; ?>
</section>

<!-- ページネーション -->
<?php
wp_pagenavi(array('query' => $pager_query));
// クエリリセット
wp_reset_postdata();
?>


3. どういう仕組みか

手っ取り早く、ウェブページを制作したい人は、以下を読まなくても問題ありません。コピペするだけで問題なく動作すると思います。

以下では「なぜ、こんな複雑な仕組みが必要か?」を説明していきます。
ただ、面倒なうえ、面白みはあまりありません。

画像2

上記が、大まかな「クエリ」と「分岐」の外観図です。
上記を参照しながらコードを見て頂ければ、どんな動作をしているか、およそ理解いただけると思います。
(この note では、基本的なクエリの書き方などの説明は割愛します。)


3-1. 2つのクエリではダメ!

【i-A-1. 固定ページが存在する場合】では、2つのクエリを使って「投稿」を表示しています。

まず「固定ページ抽出クエリ」の結果を表示し、次に「通常ページ抽出クエリ」の結果を、つづけて表示しています。

全体が9ページに収まるように、
 (1) 固定ページの件数(3)を取得する
 (2) 1ページあたりの表示数(9)から、固定ページの数(3)を引く
 (3) posts_per_page = 差(6) で、通常投稿を抽出する
という手順を取っています。

この手順自体は色んなページで紹介されています。しかし、これだけでは片手落ちです。

1ページ目はこれで問題ありませんが、2ページ目以降も「'posts_per_page' => 6」のクエリを実行すると、1ページあたり6件しか投稿が表示されない事態に陥ります。


3-2. 3つのクエリでもダメ!

では、2ページ目以降で「'posts_per_page' => 9」のクエリを実行するように条件分岐すれば十分でしょうか?

答えはNOです。

それだけでは、1ページ目の「ページャ」が狂ってしまいます。

「'posts_per_page' => 6」のクエリを元に、ページャを生成すると「全記事数を6で割った分だけページが存在する」と認識されます。そのため、実際より多くページャが表示され、リンクを踏むとエラーが表示されてしまいます。

画像3

この事態を防ぐためには、ページャ用のクエリを個別に実行するしかないように思えます。

【i-A 1ページ目の場合】であろうと【i-B 2ページ目以降の場合】であろうと関係なく、「'posts_per_page' => 9」で実行した共通クエリを元に、ページャを生成します。こうすれば、ページ数がズレることはありません。

3-3. 2ページ目以降のクエリが曲者!

さて、ページャ用のクエリ(Q1)も、固定投稿用のクエリ(Q2)も用意しました。通常投稿用のクエリは、1ページ目(Q3-1)は「posts_per_page = 6(引き算の結果)」、2ページ目以降(Q3-2)は「posts_per_page = 9」になるよう設定しました。これで十分でしょうか?

実はまだ十分ではありません。2ページ目以降のクエリは「posts_per_page = 9」ではダメなのです。

たとえば「固定3件・表示数6件」の場合、2ページ目は「7」「8」「9」と始まらなければいけません。

画像5

しかし、9件区切りでクエリを実行すると、2ページ目は「10」「11」「12」と始まってしまいます。そのため、本来表示されるべき「7」「8」「9」が、どのページにも表示されず虚空に消えてしまいます。

この問題を解決するのが、コード中の下記部分です。

'offset' => (get_query_var('paged') - 2) * 9 + $normal_number

get_query_var('paged') はページ数、
$normal_number は1ページ目に表示した「通常投稿」の件数 を指しています。

この計算式を使うと、下図のとおり、過不足なく投稿が表示されます。

画像5

「1ページあたりの表示件数」や「固定投稿の数」が変化した場合、計算式がどう変化するか、色々考えてみてください。いかなる場合でも、ちょうどよい offset が指定されることが分かると思います。(固定投稿 = 0件 でも成立します。)

(※) このような offset の使い方を、初めて見る方も多いと思います。実は、2ページ目以降を表示するとき、クエリ内部では offset が自動的に使われています。通常は「posts_per_page × (paged - 1)」が指定されています。たとえば、1ページ5件表示・3ページ目の場合は、5×(3-1)=10 が offset に指定されており、11件目の投稿から表示されるという寸法です。この自動的に使われている offset の値を、上のようなコードで書き換えることも可能なのです。


最後に。4つのクエリの使い所を図示すると、このようになります。

画像6

「複雑なクエリと分岐」の必要性の大まかな解説は、以上です。


4. 余談

わたしはウェブ制作歴4ヶ月の初心者です。本当はもっと簡単な方法があるのに、それに気づかず遠回りなやり方を考案してしまったのかもしれません。もっと簡単な方法をご存知な方は、本note か Twitter(@yanada_nomad)まで教えて頂けますと有り難いです。

また、「クエリを4つ(同時には最大3つ)実行することで、ページ表示が重くなるのではないか?」といった観点の検証は、力不足で出来ておりません。とりあえず、「トップ固定を使用しても、表示数を変えない」という要件を満たすことだけ考えて、この note の方法を考案しました。

有志の方には、別観点からの評価をいただけますと、たいへん勉強になります。


最後に。この note の内容がお役に立ったならば、SNSで共有して頂けますと有り難いです。

他にも、web制作のノウハウをまとめた note を投稿しています。マガジン「Yanada's Note」にまとめていますので、ぜひご覧ください。

この記事が気に入ったらサポートをしてみませんか?