I am working on my first project in ruby on rails and need to implement comments and replies functionality on it. I am facing of displaying replies under each comment as well as if any reply has child replies they need to display under it. structure will be some how as follow.
first comment
Reply to first comment
reply to first comment first reply
reply to first comment
Second comment
Reply to second comment
and this nested structure continues. I have only one table for these all comments with parent key to treat as reply. table structure is as follow
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
this second comment is treated as reply for first comment and id 3 comment is treated as reply on first reply of first comment. kindly help regarding to this nested structure that how i can manage it in a best way. Comment table also has association with project table and user table. suggest the best way without gem as i already tried many of them but they are limited in depth level.
We've done this before . There's also a RailsCast about it..
The term you're looking for is recursion - self replicating.
Use You can do this with 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
This will allow the following:
#app/views/projects/show.html.erb
<%= render @project.comments %>
#app/views/comments/_comment.html.erb
<%= comment.body %>
<%= render comment.replies if comment.replies.any? %>
The recursion occurs with render comment.replies
-- it will continue to loop through the replies
until there are no more. Although this will take some DB processing to do, it will display the comments with nesting.
--
If you wanted to add a reply etc, you just have to populate the "parent" 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 %>
The above will submit to the comments#create
action:
#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
This is a rough outline of one approach to this (some elements may be missing, there may well be better ways and there may be glaring errors but hopefully this could be useful):
=> Comment has_many :replies, dependent: :destroy
=> A comment will also need to accepts_nested_attributes_for :replies
=> Reply belongs_to :comment
=> Both will obviously need to belongs_to :user
=> A Comment will need to belong_to
a post/article etc.
In Routes.rb you may want to nest replies within comments
resources :comments do
resources :replies
end
How you integrate these with your post/article model is another question.
We'll need a 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
And a replies controller:
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
You can then build some views based on the above. I should add that the closure_tree
gem does seem like a useful one to look at for this. Having used the Mailboxer gem previously I would not recommend that though - as customising it is not always straightforwards.
I don't necessarily want to only suggest a gem, since you've said that you've already done your homework there, but I've used the mailboxer gem, ( https://github.com/mailboxer/mailboxer ), before for the same sort of use case to good effect. Admittedly, I had to hack it a little bit to handle some edge cases, but I think in your particular scenario, it would handle things just fine. Perhaps more to the point, even if you have to make some changes, that's probably better than rolling your own from scratch.
With that said, the data structure you've described is enough, in it's essentials to do what you're asking for. The final step would just be to set up the association on your Rails model:
class Comment < ActiveRecord::Base
has_one :parent, class: 'Comment'
end
With that set up, you just need to make sure your views display your threading properly.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.