WordPress で Ajax:コメント取得編

元ネタはお馴染み WordPress フォーラム から「コメントの表示スクリプト」です。コメントを新着順に数件表示しておいて、全部読みたい方には[全部読む]ボタンをクリックしてもらおうという作戦です。wpxtreme 的にはページネーションとかチラ見せはメンドくさいので全部見せといたらええやん!と思いますが、こういった需要は多いハズなのでやってみます。記事書きながらのカスタマイズです、できるかな。

ご要望により、コメントの表示形式を以下の 3タイプご用意いたしました。

  1. コメント全部表示
  2. 始めから指定件数分表示
  3. 2. の残りを表示

コメント表示の仕組みは TwentyEleven に倣います

  1. single.php に
    <?php comments_template( '', true ); ?>
    と書く。
  2. テーマに comments.php を用意しておく。
  3. comments.php 内に
    wp_list_comments( array( 'callback' => 'twentyeleven_comment' ) );
    のように書いて、各コメントの表示はコールバック関数にお任せする。このコールバックは皆様オリジナルのものでも良いです。Codex には非推奨って書いてあって何かコワいですけど 本家 Codex では Use with caution なので、コールバックの意味をわかってて使うなら何も問題ないです。っていうか知らんでも問題ないよ大丈夫◎

これ、基本形ね。

コメントの表示をコントロールするには

始めに5件だけ表示しておくなら[管理画面|設定|ディスカッション]で

1ページあたり[5]件のコメントを含む複数ページに分割し、[最後]のページをデフォルトで表示する 

にしとけば OK ◎ ついでに、新しいコメントから表示しないと変なので

[新しい]コメントを各ページのトップに表示する
にしましょう。って、これだと全然 OK じゃないぞ。

フォーラムでもコメントされているように

6件コメントがあると、最初の表示で1件、次のページで5件とかっこ悪い表示になってしまう

ので何とかします。

コメントの表示に使ってる wp_list_comments() ですが、実はこの wp_list_なんちゃら の一覧表示系関数には独自の Walker を指定することができます。Walker っていうのは一覧する項目をひとつづつテクテク辿って表示してくれるかわいいヤツです。コメントの場合は Walker_Comment クラス(wp-includes/comment-template.php)がソレですね。これを拡張してオリジナルの Walker をババーッと作ってしまえば、フィルターでチマチマ表示をカスタマイズする必要も無くなります。

オレ様 Walker を作ろう

何でもそうですけど、モノを作るときは使う側の立場で作りましょう。作る側の都合で作るとロクなものはできません。ということで、オレ様 Walker を使う場合に

wp_list_comments( array( 
  'callback' => 'twentyeleven_comment',
  'walker' => new My_Walker_Comment,  // これがオレ様 Walker
  'display' => 'initial',  // 未指定か initial で始めから指定件数分、rest で残り全部、all で始めから全部を表示します
) );

みたいに書きさえすれば、後は良きにはからってくれるよう頑張って作ります。始めの5件とか表示のときは display パラメータ無しか 'initial' 指定で OK。残り全部のコメントを表示するときに display パラメータを 'rest' にします。

カテゴリーによってコメントを全表示にしたり、チラ見せしたりを切り替えるなら以下のようにします。

wp_list_comments( array( 
  'callback' => 'twentyeleven_comment',
  'walker' => new My_Walker_Comment,  // これがオレ様 Walker
  'display' => in_category( 全表示したいカテゴリの ID, スラッグ, 名前等 ) ? 'all' : 'initial', 
) );

てことでオレ様 Walker ができましたのでテーマの funcrions.php に以下をペッとしてください。長いけど、オリジナルの paged_walk() をちょこっとイジッただけです。

<?php
class My_Walker_Comment extends Walker_Comment {
  function paged_walk( $elements, $max_depth, $page_num, $per_page ) {
    /* sanity check */
    if ( empty($elements) || $max_depth < -1 )
      return '';

    $args = array_slice( func_get_args(), 4 );
    $output = '';

    $id_field = $this->db_fields['id'];
    $parent_field = $this->db_fields['parent'];

    if ( !empty($args[0]['reverse_top_level']) )
      $elements = array_reverse( $elements );

    $per_page = (int)$per_page;
    if ( 0 == $per_page )
      $per_page = get_option( 'comments_per_page' );
    $count = -1;
    $total_top = count( $elements );
    $paging = $total_top > $per_page;
		
   $display = 'initial';
      if ( ! empty( $args[0]['display'] ) )
        $display = $args[0]['display'];
			
      switch ( $display ) {
        case 'initial':
          $start = 0;
          $end   = $per_page;
          break;
        case 'rest':
          $start = $paging ? $per_page : $total_top;
          $end   = $total_top;
          break;
        default: // 'all'
          $start = 0;
          $end   = $total_top;
	  break;
    }

    // flat display
    if ( -1 == $max_depth ) {
      $this->max_pages = $paging ? 2 : 1;
      $empty_array = array();
      foreach ( $elements as $e ) {
        $count++;
        if ( $count < $start )
          continue;
        if ( $count >= $end )
          break;
        $this->display_element( $e, $empty_array, 1, 0, $args, $output );
      }
      return $output;
    }
      
    $top_level_elements = array();
    $children_elements  = array();
    foreach ( $elements as $e) {
      if ( 0 == $e->$parent_field )
        $top_level_elements[] = $e;
      else
        $children_elements[ $e->$parent_field ][] = $e;
    }

    $total_top = count( $top_level_elements );
    $paging = $total_top > $per_page;
    $this->max_pages = $paging ? 2 : 1;
		
      switch ( $display ) {
        case 'initial':
          $start = 0;
          $end   = $per_page;
          break;
        case 'rest':
          $start = $paging ? $per_page : $total_top;
          $end   = $total_top;
          break;
        default: // 'all'
          $start = 0;
          $end   = $total_top;
	  break;
    }

    if ( !empty($args[0]['reverse_children']) ) {
      foreach ( $children_elements as $parent => $children )
        $children_elements[$parent] = array_reverse( $children );
    }

    foreach ( $top_level_elements as $e ) {
      $count++;

      if ( $end >= $total_top && $count < $start )
        $this->unset_children( $e, $children_elements );

      if ( $count < $start )
        continue;

      if ( $count >= $end )
        break;

      $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
    }

    if ( $end >= $total_top && count( $children_elements ) > 0 ) {
      $empty_array = array();
      foreach ( $children_elements as $orphans )
        foreach( $orphans as $op )
          $this->display_element( $op, $empty_array, 1, 0, $args, $output );
    }

    return $output;
  }
}?>

なお、パーマリンクでのコメントページ指定 /comment-page-1/ とかは一切無視しています。

[すべてのコメントを表示]ボタンを作る

comments.php で display パラメータ無しか 'initial' の wp_list_comments() を書いた下辺りに以下のコードを書きます。

カテゴリーによってコメントを全表示にしたりする場合は、始めの if 文に条件を追加します。

<?php if ( get_comment_pages_count() > 1 ) : ?>
<?php 
/* カテゴリーによって全表示とかする場合は、こっちの書き方で!
if ( get_comment_pages_count() > 1 && ! in_category( 全表示するカテゴリの ID, スラッグ, 名前等 ) ) : 
*/
?>
<div id="get-rest-of-comments"><a href="#">すべてのコメントを表示</a></div>
<script type='text/javascript'>
//<![CDATA[ 
jQuery(document).ready( function($) {
  $('#get-rest-of-comments a').click( function() {
    $(this).replaceWith('<span class="loading">Loading...</span>');
	
    $.post( '<?php echo site_url(); ?>', {
        get_rest_of_comments: 1,
        post_id: <?php echo get_the_ID(); ?>
      }, function(data) {
        $('#get-rest-of-comments').remove();
        $('#comments .commentlist').append(data); // 各コメントの li 要素の親まで辿って append してね!
      }
    );
	
    return false;
  });
});
//]]>
</script>
<?php endif; ?>

簡単に説明すると、

  1. 表示し切れてないコメントの残りがあるなら、
  2. [すべてのコメントを表示]リンクを表示して、
  3. ソレがクリックされたら、
  4. リンクを「Loading...」に変更して、
  5. get_rest_of_comments という合い言葉と記事 ID をパラメータにして Ajax !
  6. 残りのコメントを取得したら余計なものは削除して、
  7. コメントの最後に追加します。
  8. あぁっ!このクリックは無かったことにしといてね!

てな感じです。

Ajax で残りのコメント全部を取得する

テーマの functions.php に以下をペッとします。

<?php
add_action( 'init', 'my_ajax' );
function my_ajax() {
  if ( ! isset( $_POST['get_rest_of_comments'] ) )
    return;

  if ( 'XMLHttpRequest' == $_SERVER['HTTP_X_REQUESTED_WITH'] ) {
    @header('Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );

    $post_id = (int) $_POST['post_id'];
		
    $GLOBALS['post'] = get_post( $post_id );
		
    $comments = get_comments( array(
      'post_id' => $post_id, 
      'status' => 'approve', 
      'order' => 'ASC',
    ) );
    
    wp_list_comments( array( 
      'callback' => 'twentyeleven_comment',
      'walker' => new My_Walker_Comment,
      'display' => 'rest',
      ), $comments );
  }
	
  exit();
}
?>

管理画面じゃない(通常の公開画面?)からの Ajax は init アクションで処理しましょう。

簡単に説明すると、

  1. get_rest_of_comments の合い言葉が POST されてないなら無視!
  2. Ajax だったら処理します。
  3. POST された記事 ID でもって、
  4. 現在の post を設定しておいて、
  5. その記事のコメントを取得する。
  6. 残りのコメントを表示します。
  7. さいなら!

てな感じです。wp_list_comments() で表示しちゃってる内容が、先ほどの Ajax な POST

$.post( url, parameters, function(data) {} )

の結果として data に入ってきます。

CONCLUSION

  • 一覧表示系関数の出力をイジるなら、オリジナルの Walker を作るのがイマどき。
  • 公開画面側の Ajax 処理の POST 先 URL はサイトの URL でいいよ。
  • 公開画面側の Ajax 処理は init アクションでやってしまおう。
  • 公開画面側の Ajax 処理には「何の処理をするか」の合い言葉を POST してあげよう。
  • コメントは始めから全部表示しといたらいいと思うよ。
動作確認バージョン
  • WordPress 3.1.3

16 Comments

  • 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/z8YJOxSn @wordpress_fan #wordpressjp

  • 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/z8YJOxSn @wordpress_fan #wordpressjp

  • 久しぶり過ぎて期待! RT @kzxtreme: 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/q5IOjOor @wordpress_fan #wordpressjp

  • 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/z8YJOxSn @wordpress_fan #wordpressjp

  • 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/z8YJOxSn @wordpress_fan #wordpressjp

  • 【ブログ】WordPress で Ajax:コメント取得編 http://t.co/z8YJOxSn @wordpress_fan #wordpressjp

  • 【「全部読むボタン」を設置する方法】 「WordPress で Ajax:コメント取得編 | wpxtreme」 http://t.co/kh18bjQ2 #後で読め_

  • 元ネタはお馴染み WordPress フォーラム から「コメントの表示スクリプト」です。コメントを新着順に数件表示しておいて、全部読みたい方には[全部読む]ボタンをクリックしてもらおうという作戦です。wpxtreme 的にはページネーションとかチラ見せはメンドくさいので全部見せといたらええやん!と思いますが、こういった需要は多いハズなのでやってみます。記事書きながらのカスタマイズです、できるかな。 【Evernote(Local)に全文記録。どこにどんなコードを書くかの勉強になる】

  • 元ネタはお馴染み WordPress フォーラム から「コメントの表示スクリプト」です。コメントを新着順に数件表示しておいて、全部読みたい方には[全部読む]ボタンをクリックしてもらおうという作戦です。wpxtreme 的にはペー

  • 【「全部読むボタン」を設置する方法】 「WordPress で Ajax:コメント取得編 | wpxtreme」 http://t.co/kh18bjQ2 #後で読め_

  • 【お昼のブログ加筆修正】WordPress で Ajax:コメント取得編 http://t.co/PlnAYr8m

  • 【お昼のブログ加筆修正】WordPress で Ajax:コメント取得編 http://t.co/PlnAYr8m

  • WordPress で Ajax:コメント取得編  |  wpxtreme コメント表示の仕組みは TwentyEleven に倣います single.php に と書く。 テーマに comments.php を用意しておく。… http://t.co/5OBaJR6r

  • ぶくま: WordPress で Ajax:コメント取得編  |  wpxtreme: http://t.co/gs4hlRVjAd #bookmark

  • “Codex には非推奨って書いてあって何かコワいですけど 本家 Codex では Use with caution なので、コールバックの意味をわかってて使うなら何も問題ないです。っていうか知らんでも問題ないよ大丈夫◎” http://t.co/TSVHIeOhUV

  • wp_list_commentsで表示したコメントが複数ある場合、「最初に1件次ページに5件」とならないための'walker' =>'' newWalker' , 'display' => 'initial'