Make nested comment with gem closure_tree

Demo mà mình sẽ làm trong bài viết này là nested comment, mình sẽ tiếp tục làm demo dựa trên source code cũ bài viết trước của mình, sau khi hoàn thành nó sẽ như thế này nhé

1. Cài đặt

gem "closure_tree"

Thêm vào Gemfile rồi bundle install nhé

2. Chuẩn bị cho demo

Những gì bạn cần là một demo comment ajax.

$ rails g model Comment content:text user:references post:references
$ rake db:migrate

Khai báo trong routes.rb

resources :comments, only: [:create, :destroy]

Khai báo trong app/controllers/comments_controller.rb

class CommentsController < ApplicationController
  before_action :set_comment, only: [:destroy]

  def create
    @comment = Comment.create! comment_params
    respond_to do |format|
      format.html{redirect_to :back}
      format.js
    end
  end

  def destroy
    @comment.destroy
    redirect_to :back
  end

  private
  def comment_params
    params.require(:comment).permit :post_id, :user_id, :content
  end

  def set_comment
    @comment = Comment.find_by id: params[:id]
  end
end

Khai báo trong app/controllers/posts_controller.rb

def show
    @comment = Comment.new
    @comments = @post.comments
  end

Khai báo app/views/posts/show.html.erb , app/views/comments/create.js.erb , app/views/comments/_comment.html.erb Bạn có thể xem thêm ở đây: https://github.com/ptnk1995/vote_demo/pull/2/files

3. Thêm nested comment

$ rails g migration add_parent_id_to_comments parent_id:integer
$ rake db:migrate

parent_id sẽ lưu id của bình luận cha (nếu không có cha thì giá trị là null)

Chúng ta cũng sẽ tạo thêm 1 bảng mới để lưu hệ thống phân cấp bình luận.

$ rails g migration create_comment_hierarchies

Rồi khai báo trong migration này như sau:

class CreateCommentHierarchies < ActiveRecord::Migration[5.0]

  def change
    create_table :comment_hierarchies, :id => false do |t|
      t.integer  :ancestor_id, :null => false   # ID of the parent/grandparent/great-grandparent/... comments
      t.integer  :descendant_id, :null => false # ID of the target comment
      t.integer  :generations, :null => false   # Number of generations between the ancestor and the descendant. Parent/child = 1, for example.
    end

    # For "all progeny of…" and leaf selects:
    add_index :comment_hierarchies, [:ancestor_id, :descendant_id, :generations],
              :unique => true, :name => "comment_anc_desc_udx"

    # For "all ancestors of…" selects,
    add_index :comment_hierarchies, [:descendant_id],
              :name => "comment_desc_idx"
  end
end

rồi $ rake db:migrate nhé Để nó có thể nested, khai báo cần thiết là:

# models/comment.rb
acts_as_tree order: "created_at ASC"

Và cần có những sự khai báo khác, bạn có thể xem thêm ở đây: https://github.com/ptnk1995/vote_demo/pull/3/files Trong pull này điều mình muốn note lại với bạn là:

  • limit_depth: n : giới hạn cấp tối đa của nested là thứ n
<%= comments_tree_for @supports.comments_tree.hash_tree(limit_depth: 5),
     @post, @supports.comment %>
  • Muốn hiển thị tất cả những bình luận lồng nhau, để giải quyết cho vấn đề hiển thị phức tạp thế này thì khai báo helper là điều cần thiết
module CommentsHelper
  def comments_tree_for comments, post, comment_new
    safe_join(comments.map do |comment, nested_comments|
      render(comment, post: post,
        comment_new: comment_new) + tree(nested_comments, post)
    end)
  end

  def tree nested_comments, post
    unless nested_comments.empty?
      content_tag :div,
        comments_tree_for(nested_comments, post, Comment.new), class: "replies"
    end
  end
end
  • Nếu bạn muốn khi xóa comment cha, thì những comment con cũng được xóa theo như comment facebook thì gem closure_tree có hỗ trợ cho bạn phương thức:
def destroy
    @comment.descendants.each do |comment_des|
      comment_des.destroy
    end
    @comment.destroy
    redirect_to :back
  end

descendants để lấy được các comment con của nó

4. Tham khảo

Muốn tìm hiển thêm về các phương thức, cũng như cách sử dụng của gem Closure_tree bạn có thể xem thêm ở đây: Doc gem Closure_tree

5. Source Code:

Demo Gem Closure_tree - By ptnk1995