This article shows how to make a common code to create records when using Polymorphic Associations
環境情報&前提条件
- Ruby 3.2.1
- Rails 7.0.0
実装したい内容
- ポリモーフィック関連を使用して複数のテーブルへの参照を1カラムで定義する。
- SNSアプリを作成する上での通知機能の実装を想定する。UserモデルとPostモデルに参照を持つNotificationモデルのレコードを作成する。
- NotificationモデルはPostレコードに対していいね(Like)、コメント(Comment)、リポスト(Repost)された際に作成されるものとする。
- ポリモーフィックなカラムとしてNotificationテーブルには
action
カラムを定義する。
関係するアソシエーションを下記する。
notification.rb
class Notification < ApplicationRecord
belongs_to :user
belongs_to :action, polymorphic: true
end
user.rb
class User < ApplicationRecord
has_many :notifications, dependent: :destroy
end
post.rb
class Post < ApplicationRecord
belongs_to :user
end
like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
has_one :notification, as: :action, dependent: :destroy
end
comment.rb
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
has_one :notification, as: :action, dependent: :destroy
end
repost.rb
class Repost < ApplicationRecord
belongs_to :user
belongs_to :post
has_one :notification, as: :action, dependent: :destroy
end
実装手順
-
action
カラムをポリモーフィックにした場合、migrateした際にaction_type
カラムも自動生成される。 -
action_type
にはModelのクラス名を入れないとエラーになる。そのため、Likeモデルの場合は'Like'
という文字列を設定する必要がある。 -
いいねをした際(Likeレコード作成時)の通知レコードの作成は以下のように書ける。
like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
has_one :notification, as: :action, dependent: :destroy
# 以下を追記
after_create :create_notification
private
def create_notification
Notification.create(user: post.user, action: self, action_type: 'Like' )
end
end
- また、各レコード作成時の通知レコード作成処理は以下のように書ける。Likeモデルと同様に各モデルの中に処理を記述することを想定。
# いいねをした際(Likeレコード作成時)の通知レコードの作成
Notification.create(user: post.user, action: self, action_type: 'Like' )
# コメントをした際(Commentレコード作成時)の通知レコードの作成
Notification.create(user: post.user, action: self, action_type: 'Comment' )
# リポストした際(Repostレコード作成時)の通知レコードの作成
Notification.create(user: post.user, action: self, action_type: 'Repost' )
- 上記の3つはほとんど同じ処理なのでを共通化したい。差分は
action_type
に設定されている文字列のみ。-
self.class.name
とすることで、各モデルのインスタンスのクラス名を取得することができる。このメソッドを使用する。
-
-
action_type
に設定する内容を変更して共通化すると以下のように書ける。
# いいね、コメント、リポスト全てに対応した通知レコードの作成処理
Notification.create(user: post.user, action: self, action_type: self.class.name )
- この処理を使用して、Likeモデルを変更すると以下のようになる。
like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
has_one :notification, as: :action, dependent: :destroy
after_create :create_notification
private
# 以下を変更
def create_notification
Notification.create(user: post.user, action: self, action_type: self.class.name )
end
end
- しかし、このままでは複数のモデルに同じ
create_notification
を記述することになる。リファクタリングとして中途半端な状態になる。 - そのため、共通化した
create_notification
メソッドをモジュールとして切り出して更に共通化する。- 続きのリファクタリングは、以下の記事を参照ください。
- Rails7 リファクタリング 複数Modelで共通した処理をModuleに切り出す