ポリモーフィック関連

ポリモーフィック関連とは

ポリモーフィック関連とは、ある1つのカラムが複数のテーブルを参照しているようなパターンの関連を表したもの。同じようなカラムを持っているテーブルが複数ある場合、それらを1つのテーブルで管理してしまうことができる。複数のクラスのインターフェースを統一し、同じように扱えるようにする。

ポリモーフィック関連を表したテーブルは以下のような感じになる。

gyazo.com

メリット

・どのクラスに属するものかを意識することなくレコードにアクセスできる。

・特定の外部キーを持たず、1つのカラムで複数のテーブルを管理しているため、新たに管理するモデルが増えたとしてもカラムを追加することなく、has_oneの関連を定義してやるだけでよい。

*ただ単に外部キーを関連するモデルの数だけカラムとして作った場合のデメリット

→新たにタイムラインが従属するモデルを作った際、タイムラインのbelongs_toを増やし、カラムも追加しないといけない。

→タイムラインモデルのインスタンスから直接、従属しているモデル(questionなのかanswerなのか)を判断することができない。

具体例

QuestionモデルとAnswerモデルがあるとする。どちらもTimelineモデルクラスと関連しており、1つのモデルとして管理したい場合、ポリモーフィック関連で実装できる。

例)モデルの関連定義

Timelineモデル

class Timeline < ActiveRecord::Base

belongs_to :timelineable, polymorphic: true

end

Questionモデル

class Question < ActiveRecord::Base

has_one :timeline, as: :timelineable

end

Answerモデル

class Answer < ActiveRecord::Base

has_one :timeline, as: :timelineable

end



例)テーブルのカラム

timelinesテーブル

・timelineable_id ・・・リレーションしているインスタンスのid

・timelineable_type ・・・questionやanswerのような所属するクラス名

・text

questionテーブル

・id

answerテーブル

・id



例)Timelineテーブルから関連するレコードの取得

timeline = Timeline.find(1)

timelene.timelineable

=> <Question id:1…>

timeline = Timeline.find(2)

timeline.timelineable

=><Answer id:2….>

*timelineが帰属するインスタンを取得している。timeline.timelineableと記述するだけで関連先のインスタンスを取得することができる。timelineクラスのインスタンスがどのモデルに帰属しているかわからなくても.timelineableと記述するだけで関連先のインスタンスを取得できる。



例)Questionテーブル・Answerテーブルから関連するレコードの取得

question = Question.find(1)

question,timelines

=> [<Timeline id:1…>,<Timeline id:3>]

answer = Answer.find(1)

answer.timelines

=> [<Timeline id:2>,<Timeline id:4>]

*has_many定義により帰属する画像を取得してる。通常のアソシエーションとの違いは親であるquestion、もしくはanswerのidを保持するカラムはあるが、それがquestion_idやanswer_idといった名前ではない点。



例)Questionモデルに帰属するTimelineクラスのインスタンスを作成する

question = Question.create

Timeline.create(timelineable_id: question.id, timelineable_type: question.class.to_s)

=> INSERT INTO ’Timelines’ (’timelineable,id’, ‘timelineable,type’, ‘created_at’, ‘updated_at’) VALUES (1, ‘Question’, ’2018-07-31’, ‘2018-07-31’)

question = Question.create

question.timelines.create

=> INSERT INTO ’Timelines’ (’timelineable,id’, ‘timelineable,type’, ‘created_at’, ‘updated_at’) VALUES (1, ‘Question’, ’2018-07-31’, ‘2018-07-31’)

*①と②は同じ処理を意味している。

createしたQuestionクラスのインスタンスのidとクラス名を利用して、新たにtimelineをcreateしている。②はポリモーフィック関連を利用してtimelineクラスを作成している。

question.timelines.createは違和感を感じるが、questionに帰属するtimelineクラスのインスタンスを1つ作成した(レコードを追加した)という意味。

question.timelinesは複数なのになんでcreateが動かせるの?と疑問に思うが、これはポリモーフィックの文法なので仕方ない。①と違い、帰属するインスタンスのidやクラス名を入力しなくていいというメリットがある。



ポリモーフィック関連を表したテーブルに情報を保存するためには、関連するテーブルのレコードが保存された際、同時に保存されるようにしてあげる。(コールバック使う)