これは何?
Railsの課題を実施しています。ポリモーフィック関連付けを用いるという指定があったのですが、そもそも**「ポリモーフィック関連付けとは何か」**が理解できていなかったので、学んだことをノートにまとめることにしました。
なお、対応するRailsのバージョンは5.2.3
です。
概要
polymorphicとは「多形性の、多型の」という意味で、学術的な分類によく使われる単語のようです。
Railsガイドによると、ポリモーフィック関連付けとは
ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現でき
...る関連付けであり、
ポリモーフィックなbelongs_toは、他のあらゆるモデルから利用できる、(デザインパターンで言うところの)インターフェイスを設定する宣言とみなすこともできます。
ということだそうです。
また、インターフェイスとは、実装にあたって参考にしたこちらの記事によると、
このように、「違うものがある決まった振る舞い/入出力を持つことで、同じように扱えるようにすること」をダックタイピングと呼びます。そして、その「ある決まった振る舞い」「入出力の定義」のことをインターフェースと呼びます。
ということだそうです。
ER図
具体的なコードを見ていきたいと思います。
今回は、ユーザーに対する通知機能を実装しました。ログイン中のユーザーは自身に対し、
- 自身がフォローされたとき
- 自分の投稿にいいねがあったとき
- 自分の投稿にコメントがあったとき
に通知が送られます。
図で表すと、こんな感じで、
例えば、comments
とactivities
の間に、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)のインスタンスが取得できます。
ややこしいので間違えない様にしたいですね
今回は、これを利用して**「通知機能」**を作ったのですが、そちらのコードを記載すると記述が長くなってしまうので、詳細は次の記事に譲りたいと思います
[追記]次の記事はこちらです!
ポリモーフィック関連づけについて、こんな記事を紹介して頂きました!
https://spice-factory.co.jp/development/has-and-belongs-to-many-table/