「新着記事」などにページネーションを追加する方法

Cocoonカスタマイズ集
この記事は約10分で読めます。
記事内に広告が含まれています。

完成イメージ

新着記事、人気記事
新着情報

使用方法

actionオプションを追加します。

  • 新着記事
[new_list action="page"]
  • 新着情報
[info_list action="page"]
  • 人気記事
[popular_list action="page"]

実装手順

以下の手順で実装します。

/**
 * ページネーションを生成
 *
 * @param int $paged 現在のページ番号
 * @param int $max_pages 最大ページ番号
 * @param string $page_key ページ番号を表すクエリキー
 * @return string ページネーションHTML
 */
if (!function_exists('custom_pagination')):
function custom_pagination($paged, $max_pages, $page_key = 'page') {
  // 1ページ以下なら終了
  if ($max_pages < 2) {
    return '';
  }

  // 現在のURLを取得
  $current_url = $_SERVER['REQUEST_URI'];

  // 次ページのURLを生成
  $next_page = $paged + 1;
  $next_url  = remove_query_arg($page_key, $current_url);
  $next_url  = add_query_arg($page_key, $next_page, $next_url);

  // ページネーションのベースURLを生成
  $base = remove_query_arg($page_key, $current_url);
  $base = add_query_arg($page_key, '%#%', $base);

  // ページネーションを生成
  $pagination = paginate_links([
    'total'     => $max_pages,
    'current'   => $paged,
    'base'      => $base,
    'format'    => '',
    'add_args'  => false,
    'prev_text' => '<span class="screen-reader-text">' . __('前へ', THEME_NAME) . '</span><span class="fa fa-angle-left" aria-hidden="true"></span>',
    'next_text' => '<span class="screen-reader-text">' . __('次へ', THEME_NAME) . '</span><span class="fa fa-angle-right" aria-hidden="true"></span>',
  ]);

  ob_start();
  ?>
  <div class="pagination-wrapper">
    <?php if ($paged < $max_pages): ?>
      <div class="pagination-next">
        <a href="<?= esc_url($next_url); ?>" class="pagination-next-link key-btn">
          <?= apply_filters('pagination_next_link_caption', __('次のページ', THEME_NAME)); ?>
        </a>
      </div>
    <?php endif; ?>

    <?php if ($pagination): ?>
      <div class="pagination">
        <?= $pagination; ?>
      </div>
    <?php endif; ?>
  </div>
  <?php
  return ob_get_clean();
}
endif;


/**
 * ショートコード用のインスタンスIDとページ番号を取得
 *
 * @param string $base_key インスタンスのベースキー
 * @return array [$instance_id, $paged]
 */
if (!function_exists('get_shortcode_pagination_instance')):
function get_shortcode_pagination_instance($base_key) {
  static $instances = [];

  // インスタンスカウンタを初期化
  if (!isset($instances[$base_key])) {
    $instances[$base_key] = 0;
  } else {
    $instances[$base_key]++;
  }

  // インスタンスIDを生成
  $instance_id = $base_key . '_' . $instances[$base_key];

  // URLから現在ページを取得
  $paged = isset($_GET[$instance_id]) ? max(1, intval($_GET[$instance_id])) : 1;

  return [$instance_id, $paged];
}
endif;


// ショートコードHTMLの出力を変更
add_filter('do_shortcode_tag', function ($output, $tag, $attr) {
  if (!isset($attr['action']) || $attr['action'] !== 'page') {
    return $output;
  }

  // 対象タグを確認
  if (!in_array($tag, ['new_list', 'info_list', 'popular_list'], true)) {
    return $output;
  }

  static $instances_args = [];

  // インスタンスIDと現在ページを取得
  list($instance_id, $paged) = get_shortcode_pagination_instance($tag);

  // 属性を初回保存
  if (!isset($instances_args[$instance_id])) {
    $instances_args[$instance_id] = $attr;
  }

  $attr = $instances_args[$instance_id];

  // offsetを計算
  $count          = isset($attr['count']) ? intval($attr['count']) : 5;
  $attr['offset'] = $count * ($paged - 1);

  // 最大ページ数を取得
  $max_pages = 1;

  if ($tag === 'popular_list') {
    $days             = $attr['days']      ?? 'all';
    $entry_type       = $attr['type']      ?? 'default';
    $children         = $attr['children']  ?? 0;
    $author           = $attr['author']    ?? null;
    $post_type        = $attr['post_type'] ?? 'post';
    $snippet          = $attr['snippet']   ?? 0;
    $cat_ids          = (!empty($attr['cats']) && $attr['cats'] !== 'all') ? explode(',', $attr['cats']) : [];
    $exclude_post_ids = !empty($attr['ex_posts']) ? explode(',', $attr['ex_posts']) : [];
    $exclude_cat_ids  = !empty($attr['ex_cats'])  ? explode(',', $attr['ex_cats'])  : [];

    $all_records = get_access_ranking_records($days, 9999, $entry_type, $cat_ids, $exclude_post_ids, $exclude_cat_ids, $children, $author, $post_type, $snippet);

    $total     = is_array($all_records) ? count($all_records) : 0;
    $max_pages = ceil($total / $count);
  } else {
    $query_args                   = $attr;
    $query_args['posts_per_page'] = $count;
    $query_args['paged']          = $paged;
    unset($query_args['offset']);

    $query     = new WP_Query($query_args);
    $max_pages = $query->max_num_pages;
    wp_reset_postdata();
  }

  // 一覧を出力
  switch ($tag) {
    case 'new_list':
      $output = new_entries_shortcode($attr);
      break;

    case 'info_list':
      $output = get_info_list_shortcode($attr);
      break;

    case 'popular_list':
      $output = popular_entries_shortcode($attr);
      break;
  }

  // ページネーションを生成
  if ($max_pages > 1) {
    $output .= custom_pagination($paged, $max_pages, $instance_id);
  }

  wp_reset_postdata();

  return $output;
}, 10, 3);

補足

offsetオプションを利用してページ切り替えが可能です。
その際、do_shortcode_tag()を用いて、ショートコードの出力時にページネーションを挿入します。

do_shortcode_tag()は WordPressコア向けに設けられた非公開のフィルターです。
これは、プラグインやテーマ開発者による使用が想定されておらず、主にWordPressの内部処理でのみ使用されるものです。
将来的に仕様が変更される可能性があるため、使用する際は注意が必要です。

さいごに

今回紹介した方法を使えば、ショートコードで出力される記事リストにページネーションを簡単に追加できます。
「次のページ」リンクや通常のページ送りも実装できるため、ユーザーにとっても見やすく、操作しやすい一覧表示が可能になります。

タイトルとURLをコピーしました