連載記事のページネーション

この記事は1年以上前に書かれました。
内容が古くなっている可能性がありますのでご注意下さい。


当サイトのブログは連載記事が多く、しかも複数の連載が同時に進行しています。その結果、ページ下部に表示される前ページと後ページへのリンクが、別の連載記事へのリンクとなってしまうことがあります。
これでは不便ですので、連載記事にページネーションを設置するためのWordPressプラグインを開発します。

11回連載した「WordPressプラグインの開発」にページネーションを設置すると、第6回の記事では以下のように表示されます。

本当は[<][>]のボタンも欲しいのですが我慢します。

さて、このような機能を実現するためには、

  • 連載の定義
  • 連載と記事の紐付け

が必要であることが分かります。そこで、連載IDと連載名を持つ連載テーブルと、連載IDと記事IDを持つ連載記事テーブルを作成し、…という考えが浮かびますが、そんな面倒なことはしません。

  • テーブルは追加しない
  • ショートコードは書かせない
  • 専用の管理画面は作らない

という方針にします。

そこで考えたのが、WordPressの標準機能として用意されているカスタムフィールドの利用です。カスタムフィールドは、記事に追加するメタデータです。
連載記事には、以下のように

series_pagination_idという決められた名前のカスタムフィールドを追加し、値の欄に連載名を記入します。
そうすると、series_pagination_idの値が同じ記事は、同じ連載に属するということになります。
そこでプラグインは、現在表示している記事がseries_pagination_idを持つ場合、series_pagination_idの値が同じ記事を探して、ページネーションを作成します。

今回作成したプラグインのPHPプログラムは以下のとおりです。

<?php
/*
  Plugin Name: Series of Posts Pagination
  Plugin URI: 
  Description: Add pagination to the series of posts.
  Version: 0.2
  Author: Midori IT Office, LLC
  Author URI: http://midoriit.com/
  License: GPLv2 or later
*/

add_action( 'wp_print_styles', 'series_pagination_css' );

function series_pagination() {

  global $post;
  $current = $post->ID;
  $meta_value = get_post_meta($current, 'series_pagination_id', true);

  if( $meta_value == '' ) {
    return;
  }

  $args = array(
    'meta_key'=> 'series_pagination_id',
    'meta_value' => $meta_value,
    'orderby' => 'date',
    'order' => 'ASC',
    'nopaging' => true );

  $pagination='';
  $series = new WP_Query( $args );
  if( $series->found_posts > 1) {
    $num = 1;
    $pagination = '<div class="series_pagination"><span>'.$meta_value.'</span> <span>';
    while( $series->have_posts() ) {
      $series->the_post();
      if( $current == get_the_ID() ) {
        if( $num > 9 ) {
          $pagination = $pagination.'<a class="current_post wide">'.$num.'</a>';
        } else {
          $pagination = $pagination.'<a class="current_post">'.$num.'</a>';
        }
      } else {
        $permalink = esc_url( apply_filters( 'the_permalink', get_permalink() ) );
        if( $num > 9 ) {
          $pagination = $pagination.'<a class="wide" href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
        } else {
          $pagination = $pagination.'<a href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
        }
      }
      $num++;
    }
    $pagination = $pagination.'</span></div>';
  }
  wp_reset_postdata();

  echo $pagination;
}

function series_pagination_css() {
  $css_file = plugins_url( 'series-pagination.css', __FILE__ );
  wp_enqueue_style( 'series_pagination', $css_file );
}

?>

まず最初に、このプラグインが使用するCSSを読み込むためのハンドラを登録します。

add_action( 'wp_print_styles', 'series_pagination_css' );

実際にCSSを読み込むのはseries_pagination_css()関数です。

function series_pagination_css() {
  $css_file = plugins_url( 'series-pagination.css', __FILE__ );
  wp_enqueue_style( 'series_pagination', $css_file );
}

CSSについては後で解説します。

ページネーションを表示するのはseries_pagination()関数です。
最初に、

global $post;
$current = $post->ID;
$meta_value = get_post_meta($current, 'series_pagination_id', true);

if( $meta_value == '' ) {
  return;
}

現在の記事にカスタムフィールドseries_pagination_idがあるかどうか調べ、なければ何もせずreturnします。

次に、

$args = array(
  'meta_key'=> 'series_pagination_id',
  'meta_value' => $meta_value,
  'orderby' => 'date',
  'order' => 'ASC',
  'nopaging' => true );

$pagination='';
$series = new WP_Query( $args );
if( $series->found_posts > 1) {

WP_Query()で、series_pagination_idの値が同じ記事の一覧を取得します。このとき、記事の日付で昇順に検索します。
記事が2件以上見つかった場合に、以降の処理を実行します。

$num = 1;
$pagination = '<div class="series_pagination"><span>'.$meta_value.'</span> <span>';
while( $series->have_posts() ) {
  $series->the_post();
  if( $current == get_the_ID() ) {
    if( $num > 9 ) {
      $pagination = $pagination.'<a class="current_post wide">'.$num.'</a>';
    } else {
      $pagination = $pagination.'<a class="current_post">'.$num.'</a>';
    }
  } else {
    $permalink = esc_url( apply_filters( 'the_permalink', get_permalink() ) );
    if( $num > 9 ) {
      $pagination = $pagination.'<a class="wide" href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
    } else {
      $pagination = $pagination.'<a href="'.$permalink.'" title="'.get_the_title().'">'.$num.'</a>';
    }
  }
  $num++;

ページネーションの全体を、series_paginationというクラスのdiv要素とし、まずは連載名を1つのspan要素として出力します。
ループ処理の中で、同じ連載を構成する個々の記事へのリンクをa要素で出力します。このとき、現在表示している記事についてはcurrent_postというクラスにします。
また、ページ番号が9より大きい場合はwideというクラスにしています。これは、桁数が多い場合にボタンの横幅が大きくならないようにするための苦肉の策です。もっとエレガントな方法があれば教えて下さい。

最後に、

wp_reset_postdata();
echo $pagination;

WP_Query()で新しいループを始めた場合の作法としてwp_reset_postdata()を呼び出してから、できあがったページネーションをechoで出力して終了です。

CSSは以下のとおりです。

.series_pagination {
  display: block;
  width: 100%;
  padding: 5px;
  background-color: #cfc;
  font-weight: bold;
  text-align: center;
  cursor: default;
}

.series_pagination span {
  display: inline-block;
  padding: 3px;
  white-space: nowrap;
}

.series_pagination a {
  display: inline-block;
  border: 1px solid #bbb;
  border-radius: 3px;
  margin-left: 2px;
  padding: 4px 8px;
  background-color: #fff;
  text-decoration: none;
  font-family: monospace;
}

.series_pagination a.wide {
  padding: 4px 5px;
}

.series_pagination a:link {
  color: #050;
  cursor: pointer;
}

.series_pagination a:visited {
  color: #666;
  cursor: pointer;
}

.series_pagination a:hover {
  color: #ddd;
  background-color: #282;
  cursor: pointer;
}

.series_pagination a.current_post {
  color: #fff;
  background-color: #bbb;
  cursor: default;
}

以下、特記すべき点についてのみ解説します。

ページネーション全体のdivに適用する.series_paginationクラスでは、

.series_pagination {
  display: block;
  width: 100%;
  padding: 5px;
  background-color: #cfc;
  font-weight: bold;
  text-align: center;
  cursor: default;
}

要素をブロック要素とし、横幅を一杯に広げ、テキストをセンタリングします。

ページネーションの前半のタイトル部分のspanと、後半のボタン部分をまとめるspanでは、

.series_pagination span {
  display: inline-block;
  padding: 3px;
  white-space: nowrap;
}

改行を抑止します。

a要素では、

.series_pagination a {
  display: inline-block;
  border: 1px solid #bbb;
  border-radius: 3px;
  margin-left: 2px;
  padding: 4px 8px;
  background-color: #fff;
  text-decoration: none;
  font-family: monospace;
}

paddingで領域を広げ、枠線を付けてボタンらしい外観にします。

以下は、wideクラス、current_postクラス、およびlink、visited、hover擬似クラスにおけるa要素の詳細な設定です。

.series_pagination a.wide {
  padding: 4px 5px;
}

.series_pagination a:link {
  color: #050;
  cursor: pointer;
}

.series_pagination a:visited {
  color: #666;
  cursor: pointer;
}

.series_pagination a:hover {
  color: #ddd;
  background-color: #282;
  cursor: pointer;
}

.series_pagination a.current_post {
  color: #fff;
  background-color: #bbb;
  cursor: default;
}

wideクラスでは横のpaddingを小さめにし、current_postクラスはデフォルトのカーソルにします。

さて、プラグインのプログラムは以上になりますが、まだ重要な作業が残っています。プラグインをインストールし、有効化しても、series_pagination()関数が呼ばれないとページネーションは出力されません。
そこで、テーマを編集し、ページネーションを出力したい箇所でseries_pagination()関数を呼び出します。
通常は、フッタの直前だと思います。Twenty Twelveを使用しているなら、content.phpの

<footer class="entry-meta">

の前の行に、

<?php if ( is_single() ) : series_pagination(); endif; ?>

と記述します。is_single()による判定は、カテゴリー別アーカイブやタグ別アーカイブなどでページネーションを出力させないためです。

今回作成したプラグインはこちらからダウンロードできます。

※新たに、連載記事一覧ウィジェット付きのプラグインAutomatic Series Organizerを開発しましたので、今後はAutomatic Series Organizerをご利用下さい。