LoginSignup
1
0

【Rails】テーブルのリレーションを柔軟に設定する

Last updated at Posted at 2024-03-23

これはなに?

テーブル設計において、リレーションは大切な要素です。
一般的にはbelongs_tohas_many等で紐づけをします。
モデル内で紐づけるテーブルを指定することでこれを実現することができます。

本記事は、紐づけるテーブルを柔軟に設定できる「ポリモーフィック関連付け」という方法を学んだのでそのアウトプットです。

どんなことができるようになるのか?

結論、ポリモーフィック関連付けを活用するには、テーブル内に2つのカラムが必要となります。

  • xx_type: 紐づけるテーブルを指定
  • xx_id: 紐づけ先のid

xxは、関連付けるモデル名に基づいて命名されます。

例えば今、以下の3つのテーブルがあるとします。

  • 子(関連付けられるテーブル): hoge
  • 親(関連付け候補のテーブル): fuga
  • 親(関連付け候補のテーブル): piyo

ここでhoge, fuga, piyoテーブルはそれぞれポリモーフィック関連付けされているとします。

hogeテーブルにfoo_typefoo_idというカラムを用意したとき、それぞれのインスタンスにて

  • foo_type: fuga とするとfugaテーブルと紐付き、foo_idはfugaテーブルのidを参照します。
  • foo_type: piyo とするとpiyoテーブルと紐付き、foo_idはpiyoテーブルのidを参照します。

このとき、@hoge.foo によってfoo_idと一致するidを持つfuga(またはpiyo)のインスタンスを呼び出すことが可能となります。

つまり、子が紐づけられる親を選べるようになるということです。

実装例

ポリモーフィック関連付けをRailsで実現するには、親と子のモデルを以下のように実装します。

Hoge
class Hoge < ApplicationRecord
  belongs_to :foo, polymorphic: true
end

ポイントは、以下の2点です。

  • 関連付けの命名
    • 関連付け名をbelongs_toで指定します。今回はfooとしたので、このモデルにはfoo_typeとfoo_idカラムが存在します。
  • polymorphic: trueの指定
    • これにより、Hogeモデルが複数のモデルに属することを指定できます。

Fuga
class Fuga < ApplicationRecord
  has_one :hoge, as: :foo
end

ポイントは以下の2点です。

  • 紐づけモデルの指定
    • 通常のリレーションと同様に、has_oneで紐づけを指定します。
  • 関連付けの指定
    • as: で、関連付け名を指定します。今回はfooを指定しているので、子モデルのfoo_typeにfugaが指定されたら紐づけされます。

実装例

例として、Userが登録するときに、Twitterアカウントで登録するかGitHubアカウントで登録するかで紐づけテーブルを変える場合を考えます。

Userが持つカラム

  • id
  • name
  • account_type
  • account_id

Twitterが持つカラム

  • id
  • twitter_id
  • twitter_name

GitHubが持つカラム

  • id
  • github_id
  • github_url

で、それぞれのモデルを作成します。

モデルファイル

# app/models/user.rb
class User < ApplicationRecord
  belongs_to :account, polymorphic: true
end

# app/models/twitter.rb
class Twitter < ApplicationRecord
  has_one :user, as: :account
end

# app/models/github.rb
class GitHub < ApplicationRecord
  has_one :user, as: :account
end

マイグレーションファイル

class AddAccountToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :account_type, :string
    add_column :users, :account_id, :integer
    add_index :users, [:account_type, :account_id]
  end
end

使用例

# 新規のTwitterアカウントを作成する
twitter_account = Twitter.create(twitter_id: "12345", twitter_name: "example")

# twitter_accountと紐づくUserを作成
user = User.create(name: "Example User", account: twitter_account)
# このとき、account: twitter_accountによって紐づけられるTwitterモデルのオブジェクトが設定される

# user情報からTwitter名を取得
name = user.account.twitter_name
# => "example"

詳細に知りたい方向け

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