在轨道上的ruby中嵌套评论和回复

时间:2016-01-19 23:31:18

标签: ruby-on-rails ruby nested

我正在研究我在ruby on rails上的第一个项目,需要在其上实现注释和回复功能。我面临着在每条评论下显示回复以及是否有任何回复都需要在其下显示的子回复。 结构将如何如下。

first comment
       Reply to first comment
            reply to first comment first reply
       reply to first  comment
Second comment
      Reply to second comment

并且这个嵌套结构继续。 我只有一个表用于这些所有注释与父键作为回复。 表结构如下

Id | Comment_body | parent_id | user_id | project_id
1      comment                        2          2
2      comment/reply   1              2          2
3      comment/reply   2              2          2

第二条评论被视为第一条评论的回复,而第3条评论被视为第一条评论第一次回复时的回复。 请帮助我了解如何以最佳方式管理它的嵌套结构。 注释表还与项目表和用户表关联。 建议没有宝石的最佳方式,因为我已经尝试了很多,但它们的深度水平有限。

3 个答案:

答案 0 :(得分:11)

We've done this before。关于它还有一个RailsCast ..

enter image description here

您正在寻找的术语是recursion - 自我复制。

使用acts_as_tree您可以使用has_many / belongs_to

执行此操作
#app/models/comment.rb
class Comment < ActiveRecord::Base
   belongs_to :project
   belongs_to :parent,  class_name: "Comment" #-> requires "parent_id" column
   has_many   :replies, class_name: "Comment", foreign_key: :parent_id, dependent: :destroy
end

这将允许以下内容:

#app/views/projects/show.html.erb
<%= render @project.comments %>

#app/views/comments/_comment.html.erb
<%= comment.body %>
<%= render comment.replies if comment.replies.any? %>

递归发生在render comment.replies - 它将继续循环遍历replies,直到没有更多。虽然这需要进行一些DB处理,但它会显示带有嵌套的注释。

-

如果你想添加回复等,你只需要填充&#34;父母&#34; ID:

#config/routes.rb
resources :projects do 
   resources :comments #-> url.com/projects/:project_id/comments/:id
   end
end

#app/views/comments/_comment.html.erb
<%= form_for [comment.project, comment.new] do |f| %>
   <%= f.hidden_field :parent_id, comment.parent.id %>
   <%= f.text_field :body %>
   <%= f.submit %>
<% end %>

上述内容将提交至comments#create操作:

#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
   def create
      @project = Project.find params[:project_id]
      @comment = @project.comments.new comment_params
   end

   private

   def comment_params
      params.require(:comment).permit(:parent_id, :body)
   end
end

答案 1 :(得分:1)

这是对此的一种方法的粗略概述(某些元素可能缺失,可能有更好的方法,可能存在明显的错误,但希望这可能有用):

=&GT;评论has_many :replies, dependent: :destroy

=&GT;评论还需要accepts_nested_attributes_for :replies

=&GT;回复belongs_to :comment

=&GT;两者显然都需要belongs_to :user

=&GT;评论需要belong_to帖子/文章等。

在Routes.rb中,您可能希望在回复中嵌套回复

resources :comments do
  resources :replies
end

如何将这些与帖子/文章模型相结合是另一个问题。

我们需要一个CommentsController

class CommentsController < ApplicationController
  def index
   @users = User.all
   @inquiries = Inquiry.all
  end

  def new
    @user = User.find_by(id: params[:user])
    @post = Post.find_by(id: params[:post])

    @comment = @post.inquiries.new
    @message = @comment.replies.build
  end

  def create
    @user = User.find_by(id: params[:user_id])
    @post = Post.find_by(id: params[:post_id])

    @comment = Comment.create!(comment_params) #define these below
    @comment.user << @user
    redirect_to #somewhere
 end

回复控制器:

class RepliesController < ApplicationController
  before_action do
    @comment = Comment.find(params[:comment_id])
  end

  def index
    @replies = @comment.replies
    @reply = @comment.replies.new
  end

  def new
    @reply = @comment.replies.new
  end

  def create
    @reply = @comment.replies.new(reply_params)
    #redirect somewhere
  end

然后,您可以根据上述内容构建一些视图。我应该补充一点,closure_tree gem确实看起来很有用。之前我曾经使用过Mailboxer gem,但我不建议这样做 - 因为定制并不总是直截了当。

答案 2 :(得分:0)

我不一定只想推荐一个宝石,因为你已经说过你已经完成了你的作业,但是我已经使用了邮箱宝石,({{ 3}}),之前为同一种用例效果良好。不可否认,我不得不破解它以处理一些边缘情况,但我认为在您的特定情况下,它会处理好的事情。也许更重要的是,即使你必须做出一些改变,这可能比从头开始自己做更好。

话虽如此,您所描述的数据结构已经足够,它可以满足您的要求。最后一步只是在你的Rails模型上建立关联:

class Comment < ActiveRecord::Base
  has_one :parent, class: 'Comment'
end

通过该设置,您只需确保您的视图正确显示您的线程。