LoginSignup
1
0

More than 1 year has passed since last update.

【Rails】ポリモーフィック関連付けを理解する

Last updated at Posted at 2021-06-07

エンジニアになりweb開発に今月から携わるようになって初めてポリモーフィック関連付け、というのをやった。ちょっと最初理解に苦しんだのでアウトプットしておく。

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

一言でいうと、「一つのアソシエーションで複数のモデルと結びつく」ということ。ER図を用いて説明する。

まず、通常のアソシエーションでは1対1だろうが1対多だろうが一本の矢印が向かう先は1つである。
Untitled Diagram-NormalAssociation (1).png

しかしポリモーフィック関連付けでは一本の矢印から複数のテーブルにアソシエーションが組まれる。
Untitled Diagram-Polymorphic (1).png

実装方法

ポリモーフィックの実装方法はそんなに難しくない。Railsガイドにも書いてある。
https://bit.ly/3v2wXOX

①マイグレーション実行時にpolymorphic: trueを付与

以下のようなマイグレーションファイルを作る

class CreateOrders < ActiveRecord::Migration[5.2]
  def change
    create_table :orders do |t|
      t.date  :order_date
      t.references :orderable, polymorphic: true
      t.timestamps
    end
  end
end

するとテーブルにimageable_id、imageable_type、という2つのカラムができる。

  • orderable_id→外部キーにあたるカラム。ただし foreign_key: true はつけない。
  • orderable_type→どのテーブルと紐付いているのか、の情報を持つ。アソシエーションを組んだモデルのクラス名が入る。

なお、手動で両カラムを設定する方法もある。

class CreateOrders < ActiveRecord::Migration[5.2]
  def change
    create_table :orders do |t|
      t.date  :order_date
      t.bigint  :orderable_id
      t.string  :orderable_type
      t.timestamps
    end
    add_index :orders, [:orderable_type, :orderable_id]
  end
end

マイグレーションファイルを設定したら

rails db:migrate

を実行しましょう。

②アソシエーションを組む

ポリモーフィックの場合、「~able」というのがそのままアソシエーション名になる(今回で言えばorderable)

class Order < ApplicationRecord
  belongs_to :orderable, polymorphic: true
end

class Customer < ApplicationRecord
  has_many :orders, as: :orderable
end

class Organization < ApplicationRecord
  has_many :orders, as: :orderable
end

どんなメリットがあるの?

無駄な外部キーカラムができなくて済む
今回の例で言えば個人の顧客(Customer)、法人顧客(Organization)のどちらからでも購入されたらordersテーブルにデータを保存するようにしたい場合などに使える。通常個人の顧客が法人も兼ねる、というケースはまれなのでcustomer_id、organization_idと複数のカラムを作ってしまうと紐付いていないテーブルの外部キーがNULLになってしまう。これを避けることができる。

またCustomerとOrganizationで同じメソッドを定義しておけば呼び出しもラクになる。

class Order < ApplicationRecord
  belongs_to :orderable, polymorphic: true
end

class Customer < ApplicationRecord
  has_many :orders, as: :orderable

  def get_name
    customer_name
  end
end

class Organization < ApplicationRecord
  has_many :orders, as: :orderable

  def get_name
    organization_name
  end
end

こんなメソッドは通常定義しないけど、同じ名前のget_nameっていうメソッドを用意しておけば

order = Order.new
order.orderable.get_name

でCustomer, Organizationどちらのメソッドでも呼び出せる。便利。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0