使用多态关联的Rails方法是什么?

时间:2016-01-27 12:14:47

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord polymorphic-associations

我的Rails应用程序中有几个模型,它们是:

  1. 用户
  2. 照片
  3. 相册
  4. 注释
  5. 我需要向PhotoAlbum发表评论,显然属于User。我将使用polymorphic associations

    # models/comment.rb
    
    class Comment < ActiveRecord::Base
      belongs_to :user
      belongs_to :commentable, :polymorphic => true
    end
    

    问题是,为新评论描述#create行动的Rails方式是什么。我看到了两种选择。

    1。描述每个控制器中的评论创建

    但这不是一个干燥的解决方案。我可以为显示和创建注释创建一个常见的局部视图,但我将不得不重复自己为每个控制器编写注释逻辑。所以它没有工作

    2。创建新的CommentsController

    这是我想的正确方法,但正如我所知:

      

    要使其工作,您需要声明外键列和a   声明多态接口的模型中的type列

    像这样:

    # 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_idcommetable_type?我猜,commentable_type可能是模型名称。

    另外,从其他视图制作form_for @comment的最佳方法是什么?

2 个答案:

答案 0 :(得分:5)

我会使用嵌套路由和良好的旧继承。

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

  # ...
end

class Albums::CommentsController < ::CommentsController
  private
    def set_commentable
      @commentable = Album.find(param[:id])
    end
end

class Photos::CommentsController < ::CommentsController
  private
    def set_commentable
      @commentable = Photo.find(param[:id])
    end
end

虽然您可以简单地让CommentsController查看参数以确定“可评论”资源是什么,我宁愿选择此解决方案为CommentsController,否则最终会处理远远超过单个资源的情况膨胀成神级。

当你有索引动作并且需要根据父资源执行连接或者事情变得复杂时,这真的很闪耀。

使用partials制作可重复使用的表单非常简单:

# 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 %>

答案 1 :(得分:4)

你最好nesting it in the routes,然后从父类委派:

# 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| %>
  ...
相关问题