I am working on implementing polymorphic comments (these can be applied to just about any user content on the site, and is not limited to Article
instances. When creating a comment, I need to determine which commentable
it belongs to. Most of the writing I have found on this subject suggests that I use the pattern specified in find_commentable
in the code below, but this approach does not strike me as very elegant - it would seem there should be a straightforward way to unambiguously specify the commentable
a new comment is being created for, without traversing the params
set, and without string matching. Is there a better way?
In other words, is there a better way to access the commentable
object from the comment
controller in the context of a commentable
→ comment
association? Does it still work the create
method where we do not yet have a @comment
object to work with?
My models are set up as follows:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Article < ActiveRecord::Base
has_many :comments, :as => :commentable, dependent: :destroy
end
class CommentsController < ApplicationController
def create
@commentable = find_commentable
@comment = @commentable.comments.build(comment_params)
if @comment.save
redirect_to :back
else
render :action => 'new'
end
end
def index
@commentable = find_commentable
@comments = @commentable.comments
end
private
def comment_params
params.require(:comment).permit(:body)
end
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
end
end
Thank you!
I was looking for an answer to this as well and wanted to share Launch Academy's Article on Polymorphic Associations as I felt it provided the most succinct explanation.
For your application two additional options:
1. The "Ryan Bates" Method: (when you use Rails' traditional RESTful URLs)
def find_commentable
resource, id = request.path.split('/')[1, 2]
@commentable = resource.singularize.classify.constantize.find(id)
end
2. The Nested Resource:
def find_commentable
commentable = []
params.each do |name, value|
if name =~ /(.+)_id$/
commentable.push($1.classify.constantize.find(value))
end
end
return commentable[0], commentable[1] if commentable.length > 1
return commentable[0], nil if commentable.length == 1
nil
end
3. The Single Resource: (Your implementation but repeated for completion)
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With