LoginSignup
25
15

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-02-21

これは何?

Railsの課題を実施しています。ポリモーフィック関連付けを用いるという指定があったのですが、そもそも「ポリモーフィック関連付けとは何か」が理解できていなかったので、学んだことをノートにまとめることにしました。

なお、対応するRailsのバージョンは5.2.3です。

概要

polymorphicとは「多形性の、多型の」という意味で、学術的な分類によく使われる単語のようです。

Railsガイドによると、ポリモーフィック関連付けとは

ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現でき

...る関連付けであり、

ポリモーフィックなbelongs_toは、他のあらゆるモデルから利用できる、(デザインパターンで言うところの)インターフェイスを設定する宣言とみなすこともできます。

ということだそうです。
また、インターフェイスとは、実装にあたって参考にしたこちらの記事によると、

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

ということだそうです。

ER図

具体的なコードを見ていきたいと思います。
今回は、ユーザーに対する通知機能を実装しました。ログイン中のユーザーは自身に対し、

  • 自身がフォローされたとき
  • 自分の投稿にいいねがあったとき
  • 自分の投稿にコメントがあったとき

に通知が送られます。
図で表すと、こんな感じで、

Image from Gyazo

例えば、commentsactivitiesの間に、ER図の線はありませんが、comment.activityの形でcommentsからactivitiesのインスタンスが取得できる様になります。

実際に線がないところでも自由に繋がりが生み出せるのがpolymorphic(多形性の、多型の)の名前の由来なのかなと思いました。

マイグレーション

まずはマイグレーションから作成していきます。実際に作成したマイグレーションは下記の通りです。

class CreateActivities < ActiveRecord::Migration[5.2]
  def change
    create_table :activities do |t|
      t.references :user, foreign_key: true
      t.references :subject, polymorphic: true
      t.integer :action_type, null: false
      t.boolean :read, null: false, default: false

      t.timestamps
    end
  end
end

subjectカラムの中にあるpolymorphic: trueオプションがポイントです。
Railsガイドには、reference形でないマイグレーションの書き方も載っていますが、こちらは少し複雑さがましたので、今回はreference形にしました。

具体的なコード(アソシエーション)

アソシエーションはそれぞれ以下の様に定義しました。なお、ポリモーフィック関連付けに関係のない記載は除いています。

class Activity < ApplicationRecord
  belongs_to :subject, polymorphic: true
end

class Comment < ApplicationRecord
  has_one :activity, as: :subject, dependent: :destroy
end

class Like < ApplicationRecord
  has_one :activity, as: :subject, dependent: :destroy
end

class Relationship < ApplicationRecord
  has_one :activity, as: :subject, dependent: :destroy
end

上記の記述によって、例えば

$ rails c
> comment = Comment.first
> comment.activity
=> nil # activityのインスタンスがあればそれが表示される

のように取得できる様になりました。また、まだactivityのインスタンスがないので、表示できないのですが、これで

> a = Activity.first
> a.subject
# => comment や like や relationship のインスタンスが取得可能

...の形で親クラスが取得できます(activityを作成するメソッドを別途実装後)。

なお、親クラスのas: xxxxのところに指定した名前で、activity(子クラス)から親クラス(今回の場合comment, like, relationship)のインスタンスが取得できます。

ややこしいので間違えない様にしたいですね:relaxed:

今回は、これを利用して「通知機能」を作ったのですが、そちらのコードを記載すると記述が長くなってしまうので、詳細は次の記事に譲りたいと思います:grinning:

[追記]次の記事はこちらです!

ポリモーフィック関連づけについて、こんな記事を紹介して頂きました!
https://spice-factory.co.jp/development/has-and-belongs-to-many-table/

25
15
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
25
15