Reputation: 2293
I've setup two models that are commentable through same comments table:
My comments schema:
create_table "comments", force: true do |t|
t.text "body"
t.integer "commentable_id"
t.string "commentable_type"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
My comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
acts_as_votable
end
My movie model
class Movie < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
end
My book model:
class Book < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
end
My comments controller:
def index
@commentable = find_commentable
@comments = @commentable.comments
end
def create
@commentable = find_commentable
@comment = @commentable.comments.build(params[:comment])
@comment.user = current_user
if @comment.save
flash[:notice] = "Successfully created comment."
redirect_to @commentable
else
render :action => 'new'
end
end
def upvote_movie
@movie = Movie.find(params[:movie_id])
@comment = @movie.comments.find(params[:id])
@comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back}
end
end
def upvote_book
@book = Book.find(params[:book_id])
@comment = @book.comments.find(params[:id])
@comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back}
end
end
private
def find_commentable
params[:commentable_type].constantize.find(params[:commentable_id])
end
end
How can I add threading(reply to comments) to what I already have?
Here is a blog that talks about threading:http://www.davychiu.com/blog/threaded-comments-in-ruby-on-rails.html
I'm just not sure how to put the two together.
Here is what I have in my movie show view:
<%= render partial: "comments/form", locals: { commentable: @movie } %>
<% @comments.each do |comment| %>
<hr>
<p>
<strong><%= link_to comment.user.username, user_path(comment.user), :class => "user" %>
</strong> <a><%= "(#{time_ago_in_words(comment.created_at)} ago)" %></a>
</p>
<p>
<%= simple_format(auto_link(comment.body, :html => { :target => '_blank' } )) %>
<% end %>
Here is what my comment form looks like:
<%= form_for [commentable, Comment.new] do |f| %>
<%= hidden_field_tag :commentable_type, commentable.class.to_s %>
<%= hidden_field_tag :commentable_id, commentable.id %>
<p>
<%= f.text_area :body %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
Upvotes: 1
Views: 391
Reputation: 24815
I scanned the article you mentioned and found the solution there is quite limited.
The basic idea in the article is to set a comment itself as commentable. So a nested comment is actually NOT a comment of the post, but of the parent comment.
The drawbacks are apparent and unacceptable:
It's hard to get other things right. For example, posts.comments.size
is no long correct.
You'll have hard dependency on this structure. If in one day you don't want to display comments in thread but plainly, you...will kick a stone.
If you want to do it on current comment system, it's hard.
Add an extra field reply_to
to comment model, referring to other comment's id.
When adding comment, add a reply_to
id if it replied to one.
When showing, show a list of all comments with reply_to
null.
Then for each comment, show nested comments has its id. And do it recursively.
If you want to limit the nested level, you can add an extra nested_level
field, getting in from the front-end. If nest limit is 3, no comments is allowed to reply a comment with nest level of 3.
add: demo helper to render recursively
def render_replied_comments(comment)
if comment.has_reply
comments.replies.each do |reply|
render partial: 'comment', locals: {comment: reply}
render_replied_comment(reply)
end
end
end
# View
@post.top_level_comments.each do |comment|
render partial: 'comment', locals: {comment: comment}
end
Upvotes: 1
Reputation: 2583
You would add a parent_id to the comments model that is a self-referencing relationship. So parent comments would have a parent_id of nil and all child comments would have a parent_id of that parent comment. You are essentially constructing a tree.
The Ancestory Gem is ideal for this or roll your own, good learning experience.
Upvotes: 0