0
0

Rails7 リファクタリング ポリモーフィックな参照を持つレコードの新規作成を共通化する

Last updated at Posted at 2023-10-01

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メソッドをモジュールとして切り出して更に共通化する。
0
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
0
0