ポリモーフィック関連付けって何。

ポリモーフィズムとは

Polymorphism、意味は多様性。オブジェクティブ指向の3大要素の一つ。 一言でいうと、オブジェクトの実態を気にせず、メソッドを呼び出し、そのオブジェクトごとに振舞ってもらうことだそう。 例えばサイドスローの投手、アンダースローの投手、オーバースローの投手がいたとして、3人の投げ方を見たいときに、

サイドスローで投げてください」 「アンダースローで投げてください」 「オーバースローで投げてください」

とそれぞれの振る舞いごとに命令をいうのではなく、3人共に

「投げてくださいください。」

と命令した方が楽。

実装したいコードを日本語で表すと、

 

定義 投げる

インスタンス 投げる.オーバー

 

インスタンス 投げる.サイド

 

インスタンス 投げる.アンダー

オブジェクトごとにいちいちメソッドを定義するのが面倒だから、一度メソッドを定義(今回の場合だと投げる)して、インスタンスを生成した方が楽。

このように、「違うものがある決まった振る舞い/入出力を持つことで、同じように扱えるようにすること」をダックタイピングと呼ぶ。

そして、その「ある決まった振る舞い」「入出力の定義」のことをインターフェースと呼ぶ。 ポリモーフィック関連とは上記のポリモーフィズムの思想の元、モデルを関連づけること。

例えば、RUNTEQとSUBARUがTimesChannelを所有している場合、TimesChannelがSubaruのものなのか、Runteqのものなのかの二通りある。

ポリモーフィックを使わない場合、

Runteq.rb

class Runteq < ApplicationRecord
  has_many: times_channels
end
Runteq.rb

class Subaru < ApplicationRecord
  has_many: times_channels
end
Runteq.rb

class TimesChannel < ApplicationRecord
  belongs_to: Runteq
  belongs_to: Subaru
end

上記3つのコードで一応、関連はつけられているが、問題が2つあって、

  1. TimesChannelの投稿者の情報を取得する場合、投稿者がSubaru(今回はたまたまSubaru)なのか、Runteqなのか確認しなければならない。
  2. TimesChannelを使う人のモデル(今回はたまたまSubaru)が増えた場合、TimesChannelモデルにカラムを追加して関連づけなければならない。

これらをの問題を解決してくれるのが、ポリモーフィック関連だそう。

使ってみますか。

ポリモーフィック関連を実装してみる

実装は簡単です。

create_post.rb

class CreateTimesChannel < ActiveRecord::Migration[5.1]
  def change
    create_table :times_channels do |t|
      t.string  :name
      t.references :creatable, polymorphic: true, index: true
      t.timestamps
    end
  end
end

他のモデルと繋げるためのフックとして、referenceを用いて参照するためのキーをカラムに追加します。

そのときにオプションでpolymorphicをtrueにするだけです。

これでcreatableを使って関連した投稿者を取得できます。

この時、自動でcreatable_typeってのが追加されます。ここでチャンネル作成者のクラスを判断してるみたいです。

schema.rb
 create_table "times_channels", force: :cascade do |t|

    t.string "name"
    t.string "creatable_type"
    t.integer "creatable_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["creatable_type", "creatable_id"], name: "index_times_channels_on_creatable_type_and_creatable_id"
  end

あとは、各々のモデルで関連づけるだけ。

class TimesChannels < ApplicationRecord
  belongs_to :creatable, polymorphic: true
end

class Subaru < ApplicationRecord
  has_many :times_channels, as: :creatable 
end

class Runteq < ApplicationRecord
  has_many :times_channels, as: :creatable
end

今後TimesChannelモデルと結び付けたいモデルが現れたら、下記コードをモデルの中に追加するだけで、TimesChannelモデルと結びつけることができる。

 has_many :times_channels, as: :creatable 

つまり、いちいちTimesChannelモデルにbelongs_toというコードを追加しなくて良くなる。

これをなんかそれっぽくいうと、

「既存コードはいじらず」「新しく追加するコード側のインターフェースのみ気を付ければ良い」ようになる。そうです。

Railsのポリモーフィック関連 初心者→中級者へのSTEP10/25 - Qiita

Railsのポリモーフィック関連とはなんなのか - Qiita