我的 Rails 应用程序中有几个模型,它们是:
我需要发表属于
Photo
或Album
的评论,并且显然总是属于User
。我将为此使用多态关联。
# models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
问题是,Rails 如何描述新评论的
#create
操作。我看到有两种选择。
1。描述每个控制器中的注释创建
但这不是一个 DRY 解决方案。我可以制作一个通用的部分视图来显示和创建注释,但我将不得不重复自己为每个控制器编写注释逻辑。所以它不起作用
2。创建新的 CommentsController
这是我猜的正确方法,但据我所知:
要实现此目的,您需要声明外键列和 在声明多态接口的模型中键入列
像这样:
# 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
所以,当我编写非常简单的控制器时,它将接受来自远程表单的请求:
# 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
我如何获得
commentable_id
和 commetable_type
?我猜,commentable_type
可能是一个型号名称。
另外,从其他视图制作
form_for @comment
的最佳方法是什么?
我会将嵌套路由与良好的旧继承一起使用。
Rails.application.routes.draw do
resources :comments, only: [:show, :edit, :update, :destroy] # chances are that you don't need all these actions...
resources :album, shallow: true do
resources :comments, only: [:new, :index, :create], module: 'albums'
end
resources :photos, shallow: true do
resources :comments, only: [:new, :index, :create], module: 'photos'
end
end
class CommentsController < ApplicationController
before_action :set_commentable, only: [:new, :index, :create]
def create
@comment = @commentable.comments.new(comment_params) do |c|
c.user = current_user
end
# ...
end
private
def set_commentable
@commentable = commentable_class.find(params[:id])
end
def commentable_class
raise("commentable_class must be implemented by subclasses")
end
# ...
end
module Albums
class CommentsController < ::CommentsController
private
def commentable_class
Album
end
end
end
module Photos
class CommentsController < ::CommentsController
private
def commentable_class
Photo
end
end
虽然您可以简单地让
CommentsController
查看参数来确定什么是“可注释”资源,但我更喜欢这个解决方案,因为 CommentsController
否则最终会处理远远不止一个资源,并且可能会膨胀为上帝类。
当您有索引操作并需要基于父资源执行联接或当事情变得复杂时,这确实非常有用。
使用部分内容制作可重复使用的表单非常简单:
# views/comments/_form.html.erb
<%= form_for([commentable, commentable.comment.new]) do |f| %>
<%= f.label :body %>
<%= f.text_field :body %>
<% end %>
然后你可以像这样包含它:
<%= render partial: 'comments/form', commentable: @album %>
你最好将其嵌套在路由中,然后从父类中进行委托:
# 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
系统会自动为您填写。
为了给自己一个
@comment
对象,你必须使用:
#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| %>
...