投稿日:2024/5/21
更新日:2024/11/4
お疲れ様です!
今回は Rails ガイドにも記載されているポリモーフィック関連を利用したいいね機能の作成方法についての記事になります。
ポリモーフィック関連を利用することで、複数のモデルに対して共通の「いいね」機能を簡単に実装することができます。
本来いいね機能を作成しようとすると、各モデル(例えば、Post や Comment)に対して個別に「いいね」テーブルを作成する必要があります。例えば、以下のような設計になります。
class Post < ApplicationRecord
has_many :likes
end
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
end
class CreateLikes < ActiveRecord::Migration[6.1]
def change
create_table :likes do |t|
t.references :post, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
いいねを行う対象モデルが 1 つの場合であればこの設計が適切ですが、複数モデルに対していいね機能を実装する場合各モデルに対して個別に「いいね」機能を実装する必要があり、モデルが増えるたびに同じようなテーブルと関連を追加する必要が出てきます。
例えば Post、Comment、Photoモデルに対していいねできるようにしたい場合、PostLike、CommentLike、PhotoLikeと三つ似たような役割のモデルを作成する必要があります。
このような場合にポリモーフィック関連を利用することで、共通の「いいね」テーブルを作成し、どのモデルに対しても簡単に「いいね」機能を追加できます。
class Like < ApplicationRecord
belongs_to :likeable, polymorphic: true
belongs_to :user
validates :user_id, uniqueness: { scope: [:likeable_type, :likeable_id], message: "has already liked this item" }
## 一意性を持たせるバリデーションを付与した
end
class Post < ApplicationRecord
has_many :likes, as: :likeable
end
class Comment < ApplicationRecord
has_many :likes, as: :likeable
end
class CreateLikes < ActiveRecord::Migration[6.1]
def change
create_table :likes do |t|
t.references :user, null: false, foreign_key: true
t.references :likeable, polymorphic: true, null: false
t.string :likable_type
t.timestamps
end
end
end
この設計の場合、LikableType にはいいねをしたモデル名が String 型で入ります。Post モデルにいいねしたのであれば"Post"、Comment モデルに対していいねしたのであれば"Comment"といった感じです!
likeable にはいいねしたモデルの中のどのレコードに対してかを入れます。Post モデルの id が 100 のものにいいねをつけたとすると
{
likable: 100,
likable_tyoe: Post
}
のようになります。
こうすることでモデルがいいね機能を追加したいモデルが複数あったとしても、いいねのモデルは作成しなくても済みます。
ポリモーフィック関連を利用したいいね機能は、以下のようなシーンで特に有効です。
今回はちょっと発展的なDB設計についてまとめました。
ただし、この設計を行うことによるデメリットもあるので、それについてもまた今度まとめたいなぁと思います。