Railsでは、ActiveRecordのhas_many
のas
オプションとbelogns_to
のpolymorphic
オプションを使うことで、DBのポリモフィックのリレーションを簡単に実装することができます。
動作確認
- Rails 4.1
- ActiveRecord 4.1
目次
1. ポリモフィックリレーションとは
説明のために次のER図を実装してみます。
記事(Article)とイベント(Event)にそれぞれコメント(Comment)を複数つけることができます。
記事用のコメントテーブル、イベント用のコメントテーブルとそれぞれ作ると、それぞれ同じような処理を記述しなければいけないため、コメントテーブルは参照先となるテーブルをArticleかEventのどちらかをもつようにします。
このような関係を「ポリモフィック関連」と言います。
2. ポリモフィックなテーブルの作成
まず、Eventモデルとテーブルを作成します。rails g model event name:string content:text
次に、Articleモデルとテーブルを作成します。
rails g model article title:string body:text
最後に、Commentモデルとテーブルを作成します。
このとき、参照先のIDを格納する外部キーのxxx_id
と参照先のモデル名を格納するxxx_type
という2つのカラムが必要になります。
rails g model comment content:text commentable_id:integer commentable_type:string
インデックスを追加しておきます。
# db/migrate/yyyymmddhhMMss_create_comments.rb class CreateComments < ActiveRecord::Migration def change create_table :comments do |t| t.text :content t.integer :commentable_id t.string :commentable_type t.timestamps end add_index :comments, [:commentable_id, :commentable_type] end end
マイグレーションを実行します。
rake db:migrate
3. モデルにhas_manyとbelongs_toを追加する
belogns_to
をpolymorphic
オプションをつけて記載します。デフォルトでは
belongs_to
の引数のcommentable
と上記のxxx_id
とxxx_type
は同じにする合わせる必要があります。(class_nameやforeign_keyオプションを使うことで変更可能です。)
# app/models/comment.rb class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end
次に、EventモデルとArticleモデルにhas_many
をas
オプションをつけて記載します。
# app/models/event.rb class Event < ActiveRecord::Base has_many :comments, as: :commentable end # app/models/article.rb class Article < ActiveRecord::Base has_many :comments, as: :commentable end
これで、ポリモフィックの設定は完了です。
4. 使えるようになるメソッド
ポリモフィック関連を設定するとCommentを作成すると自動的に、下記のようにcommentable_id
とcommentable_type
に適切な値が設定されます。
# EventのCommentを作成すると自動的にCommentレコードのcommentable_idとcommentable_typeにEventの情報が設定される event = Event.create name: "event1" event_comment = event.comments.create content: "This event is awesome!" event_comment # => <Comment id: 1, content: "This event is awesome!", commentable_id: 1, commentable_type: "Event", created_at: ...> # 同様に、ArticleのCommentを作成すると自動的にCommentレコードのcommentable_idとcommentable_typeにArticleの情報が設定される article = Article.create title: "article1" article_comment = article.comments.create content: "This article is great!" article_comment # => <Comment id: 2, content: "This article is great!", commentable_id: 1, commentable_type: "Article", created_at: ...>
5. ポリモフィックの画面を作成
おまけとして、ポリモフィック関連のCommentsコントローラーの実装方法も紹介します。次のようなルーティングになっている場合、Commentsコントローラーでは、動的にArticleクラスかEventクラスを取得する必要があります。
# config/routes.rb resources :articles do resources :comments end resources :events do resources :comments end
load_commentable
メソッド内で、リクエストのURLからArticleかEventを判定し、ポリモフィック関連に合わせて動的にモデルを読み込むようにしています。
# app/controllers/comments_controller.rb class CommentsController < ApplicationController before_action :load_commentable def index @comments = @commentable.comments end def new @comments = @commentable.comments.new end def create @comment = @commentable.comments.new(comment_params) if @comment.save redirect_to @commentable, notice: "コメントしました" else render :new end end private # URLからEventかArticleを取得する # ex: /events/:id/comments # ex: /articles/:id/comments def load_commentable resource, id = request.path.split('/')[1, 2] @commentable = resource.singularize.classify.constantize.find(id) end # 他の方法 # def load_commentable # klass = [Event, Article].detect { |c| params["#{c.name.underscore}_id"] } # @commentable = klass.find(params["#{klass.name.underscore}_id"]) # end def comment_params params ... end end
以上です。
参考文献
- Active Record Associations — Ruby on Rails Guides
- Ruby on Rails API
- Rails3 レシピブック 190の技