I have few models in my Rails application, which are:
I need to make comments belog to either to Photo
or Album
, and obviously always belong to User
. I'm going to use polymorphic associations for that.
# models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
The question is, what is the Rails way to describe #create
action for the new comment. I see two options for that.
1. Describe the comment creation in each controller
But ths is not a DRY solution. I can make one common partial view for displaying and creating comments but I will have to repeat myself writing comments logic for each controller. So It doesn't work
2. Create new CommentsController
This is the right way I guess, but as I aware:
To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface
Like this:
# schema.rb
create_table "comments", force: :cascade do |t|
t.text "body"
t.integer "user_id"
t.integer "commentable_id"
t.string "commentable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
So, when I will be writing pretty simple controller, which will be accepting requests from the remote form:
# controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
@comment = Comment.new
end
def create
@commentable = ??? # How do I get commentable id and type?
if @comment.save(comment_params)
respond_to do |format|
format.js {render js: nil, status: :ok}
end
end
end
private
def comment_params
defaults = {:user_id => current_user.id,
:commentable_id => @commentable.id,
:commentable_type => @commentable.type}
params.require(:comment).permit(:body, :user_id, :commentable_id,
:commentable_type).merge(defaults)
end
end
How will I get commentable_id
and commetable_type
? I guess, commentable_type
might be a model name.
Also, what is the best way to make a form_for @comment
from other views?
You'll be best nesting it in the routes, then delegating from the parent class:
# config/routes.rb
resources :photos, :albums do
resources :comments, only: :create #-> url.com/photos/:photo_id/comments
end
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
@parent = parent
@comment = @parent.comments.new comment_params
@comment.save
end
private
def parent
return Album.find params[:album_id] if params[:album_id]
Photo.find params[:photo_id] if params[:photo_id]
end
def comment_params
params.require(:comment).permit(:body).merge(user_id: current_user.id)
end
end
This will fill it out automatically for you.
In order to give yourself a @comment
object, you'll have to use:
#app/controllers/photos_controller.rb
class PhotosController < ApplicationController
def show
@photo = Photo.find params[:id]
@comment = @photo.comments.new
end
end
#app/views/photos/show.html.erb
<%= form_for [@photo, @comment] do |f| %>
...