公開当時、WordPressテーマSANGOのカスタマイズガイドのコメント数が2500を超えていました。ユーザーの方から「コメントを探しにくい」「投稿ページの下までスクロールするのが大変」などという声をいただいたので、サイト内の全コメント一覧ページを作りました。以下は公開中の投稿についた承認済みの親コメントを並べる例です。返信は含めません。

プラグインなしでコメント一覧を実装する

公開当時は一覧のデザインまで調整できるプラグインを見つけられず、自分でコードを書いて実装しました。以下ではPHPを使うクラシックテーマでの方法を紹介します。ブロックテーマのHTMLテンプレートにはこのPHPを貼り付けられません。

この記事では、固定ページの専用テンプレートでコメント一覧とページ送りを表示する方法を紹介します。

大まかな流れ

  1. 固定ページを新規作成し、パーマリンクをcommentsなどにする
  2. 子テーマのディレクトリ直下にpage-comments.phpというファイルを作成する
  3. page-comments.phpにコメント一覧を表示するためのコードを書く

ここではクラシックテーマの子テーマに専用テンプレートを置く方法を使います。作業前にファイルをバックアップし、検証環境で試してください。親テーマを直接変更すると、更新時に編集が失われます。

手順1:コメント一覧を載せるページを用意する

まず、コメント一覧をどこに載せるかを考えました。他のテンプレートファイル(single.phpやpage.phpなど)に依存しないようにしたかったので、専用の固定ページのファイルを作ることにしました。というわけで、まず固定ページを新規作成し、パーマリンクをcommentsにします。

手順2:page-comments.phpを新規作成

今回はcommentsというパーマリンクを設定したので、page-comments.phpというファイルを作成します。これでhttps://example.com/commentsにアクセスしたときにpage-comments.phpが選ばれます。これはPHPを使うクラシックテーマのページテンプレート階層によるものです。ブロックテーマではHTMLテンプレートの階層を確認してください。

クラシックテーマでは固定ページのスラッグに対応するテンプレートファイルを選べます。
この例では「page-comments.php」を子テーマへ置きます。固定ページ用のpage.phpより専用テンプレートが優先されます。

手順3:コードを書く

それではコードを書いていきます。まずは公開中の投稿についた承認済みの親コメントを取得して表示する例です。返信は含めません。

まずはコメント一覧を取得する

page-comments.php
<?php
$comment_args = [
    'parent' => 0,
    'type' => 'comment',
    'status' => 'approve',
    'post_status' => 'publish',
];
foreach ( get_comments( $comment_args ) as $comment ) :
?>
  ★ここにコメントを表示する★
<?php endforeach; ?>

コメント一覧はget_comments()で取得できます。引数に配列で「どのコメントを取得するか」という条件を指定します。ここでは親コメント・通常コメント・承認済み・公開中の投稿に限定します。たとえば

page-comments.php
get_comments( [
    'parent' => 0,
    'type' => 'comment',
    'status' => 'approve',
    'post_status' => 'publish',
] )

この例では通常コメント("type"=>"comment")と承認済み("status"=>"approve")に加え、親コメントと公開中の投稿へ対象を限定しています。詳しい条件は公式のリファレンスをチェックしてみてください。
例えば、"parent"=>0を指定すると返信を除く親コメントだけになります。さらに公開中の投稿へ限定するため、コードでは「post_status」に「publish」を指定しています。

コメントの内容を出力する

get_comments()によりコメント一覧のオブジェクトが配列で返ってくるので、これをforeachで出力します。例えば、以下のような形。

page-comments.php
<?php
$comment_args = [
    'parent' => 0,
    'type' => 'comment',
    'status' => 'approve',
    'post_status' => 'publish',
];
foreach ( get_comments( $comment_args ) as $comment ) :
?>
<?php echo get_avatar( $comment, 50 ); ?>
<?php echo esc_html( get_comment_author( $comment ) ); ?>
<?php echo esc_html( get_comment_date( 'Y/m/d', $comment ) ); ?>
<?php comment_text( $comment ); ?>
<?php endforeach; ?>

それぞれdivタグやpタグなどでマークアップすれば良いですね(この記事ではスタイリングは省略します)。

ちなみに「$comment->comment_content」は保存されている本文の生データです。画面へ表示するときは、comment_text()というWordPressの関数で本文を表示します。コメントのオブジェクトを渡し、WordPressの本文表示処理を通します。生データからリンクなどのマークアップが必ず失われるという意味ではありません。

ページネーションを実現する

上の図は公開当時のページ送りの表示例です。このままだと、1つのページに全てのコメントが出力されてしまいます。サーバーへの負荷、読み込み速度などを考えるとページ分割したいですね。さきほどのコードをページネーションできるように改良します。

page-comments.php
<?php
$per_page = 15;
$base_args = [
    'parent' => 0,
    'type' => 'comment',
    'status' => 'approve',
    'post_status' => 'publish',
];
$total_comments = (int) get_comments( array_merge( $base_args, [ 'count' => true ] ) );
$total_pages = max( 1, (int) ceil( $total_comments / $per_page ) );
$requested_page = filter_input( INPUT_GET, 'page_number', FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 1 ] ] );
$page = min( $requested_page ?: 1, $total_pages );
$offset = ( $page - 1 ) * $per_page;
$comments = get_comments( array_merge( $base_args, [
    'number' => $per_page,
    'offset' => $offset,
] ) );
foreach ( $comments as $comment ) :
?>
  ★ここにコメントを表示する★
<?php endforeach; ?>
<?php
echo paginate_links( [
    'base' => add_query_arg( 'page_number', '%#%', get_permalink( get_queried_object_id() ) ),
    'format' => '',
    'total' => $total_pages,
    'current' => $page,
    'prev_text' => '前へ',
    'next_text' => '次へ',
    'type' => 'list',
] );
?>

コメント一覧のページ分割には、件数の取得、現在のページ番号とoffsetの計算、paginate_links()によるリンク生成を組み合わせます。以下でそれぞれ説明します。

URLに「いま何ページ目にいるか」の情報を持たせる

URLパラメータという形でこれを実現します。たとえば2ページ目ならhttps://example.com/comments?page_number=2というURLにします。これでfilter_input( INPUT_GET, 'page_number', FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 1 ] ] )で正の整数を検証します。その後、範囲外の番号は表示可能な最終ページへ収めます。

この例ではURLの「page_number」をページ番号に使います。通常の「page」は固定ページの分割表示にも使われるため、専用の名前にしています。

合計のコメント数やページ数を地道に計算する

同じ親コメント・通常コメント・承認済み・公開中の投稿という条件で合計件数を調べるには、get_comments( array_merge( $base_args, [ 'count' => true ] ) )を使います。ページに表示するコメントは別のget_comments()で取得します。件数取得のために全コメントの配列を読み込まずに済みます。
コメントを取得するためのget_comments()の引数には「このページでは○番目のコメント(offset)から、いくつ(number)表示するか」という指示も与えてやります。

WordPressでコメント一覧を実現するコード(完成形)

ここまでの内容をまとめた完成形です。クラシックテーマの子テーマへ置くpage-comments.phpの表示例で、テーマヘッダーとフッターも含めています。既存のPHP開始タグやテーマの構造に合わせて確認してください。

page-comments.php
<?php get_header(); ?>
<?php
$per_page = 15;
$base_args = [
    'parent' => 0,
    'type' => 'comment',
    'status' => 'approve',
    'post_status' => 'publish',
];
$total_comments = (int) get_comments( array_merge( $base_args, [ 'count' => true ] ) );
$total_pages = max( 1, (int) ceil( $total_comments / $per_page ) );
$requested_page = filter_input( INPUT_GET, 'page_number', FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 1 ] ] );
$page = min( $requested_page ?: 1, $total_pages );
$offset = ( $page - 1 ) * $per_page;
$comments = get_comments( array_merge( $base_args, [
    'number' => $per_page,
    'offset' => $offset,
] ) );
?>
<h1>コメント一覧(<?php echo esc_html( $page ); ?>ページ目)</h1>
<div class="comments-list">
<?php foreach ( $comments as $comment ) : ?>
  <div class="comment">
    <?php echo get_avatar( $comment, 50 ); ?>
    <?php echo esc_html( get_comment_author( $comment ) ); ?>
    <?php echo esc_html( get_comment_date( 'Y/m/d', $comment ) ); ?>
    <?php comment_text( $comment ); ?>
  </div>
<?php endforeach; ?>
</div>
<div class="pagination">
<?php
echo paginate_links( [
    'base' => add_query_arg( 'page_number', '%#%', get_permalink( get_queried_object_id() ) ),
    'format' => '',
    'total' => $total_pages,
    'current' => $page,
    'prev_text' => '前へ',
    'next_text' => '次へ',
    'type' => 'list',
] );
?>
</div>
<?php get_footer(); ?>

foreachの中のコメント表示部分は、必要に応じて別のテンプレートパーツに分けられます。この例のCSSによる一覧デザインは各テーマで調整してください。