我需要为 post#show 和 picture#show 视图创建一个表单,以便为它们创建任何评论。我为它们创建了一个多态关联和迁移,但我需要帮助来为它们创建表单。
class Post < ApplicationRecord
has_many :comments, as: :commentable, dependent: :destroy
end
class Picture < ApplicationRecord
has_many :comments, as: :commentable, dependent: :destroy
end
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
在谷歌中我搜索了该解决方案,但我不确定它们的正确性:
<%= form_for comment do |f| %>
<%= f.hidden_field :commentable_id, value: params["id"] %>
<%= f.hidden_field :commentable_type, value: params["controller"].singularize.capitalize %>
<p>Comment: <%= f.text_area :content %></p>
<%= f.submit %>
<% end %>
处理多态关联不应该与任何其他嵌套资源有任何不同,因为您的模型是内部实现细节,而不是应用程序的公共 HTTP API 的一部分。
# config/routes.rb
resources :posts do
resources :comments, only: :create, module: :posts
end
resources :pictures do
resources :comments, only: :create, module: :pictures
end
这里我们创建了一条
POST /posts/:post_id/comments
路线和一条 POST /pictures/:picture_id/comments
路线。这会将关系放置在 URI 中,从而使资源之间的 RESTful 关系变得明确,而不是将其隐藏在请求正文中。
使用
module: :posts
将其路由到单独的控制器是可选的,但我发现使用继承比将所有内容都放入单个控制器并通过代码分支嗅探父类更干净。
# Abstract base class for nested comments
class NestedCommentsController < ApplicationController
before_action :find_commentable
def create
@comment = @commentable.comments.new(comment_params)
if @comment.save
yield @comment if block_given?
redirect_to after_save_path_for(@comment)
else
render :new
end
end
private
def commentable_class
raise "This method must be implemented by subclasses"
end
def commentable_param_key
commentable_class.model_name.singular_param_key + "_id"
end
def commentable_param
params[commentable_param_key]
end
def find_commentable
@commentable = commentable_class.find(commentable_param)
end
def after_save_path_for(comment)
comment.commentable
end
end
虽然这可能看起来很复杂,但它只是使用一种方法来避免对父类和 ActiveModel::Naming API 的引用进行硬编码以及约定优于配置。
module Posts
class CommentsController < :: NestedCommentsController
def commentable_class
Post
end
end
end
module Pictures
class CommentsController < :: NestedCommentsController
def commentable_class
Picture
end
end
end
该表单只是嵌套资源的常规表单:
<%= form_with model: [commentable, comment] do |f| %>
<p>Comment: <%= f.text_area :content %></p>
<%= f.submit %>
<% end %>
当您传递数组时,多态路由助手会将其解析为嵌套路由助手,并调用
post_comments_path(post)
或picture_comments_path(picture)
来获取表单的操作属性。
数组也被压缩,因此它适用于浅层路线只要你为本地传递nil:
[nil, comment] # comments/1