【Rails】ポリモーフィック関連を使って複数のテーブルに対してのいいねを実装

2023/04/04に公開

概要

複数のテーブルに対してのいいねを実現するには、いくつかの方法がある(都度テーブルを増やす、STI、ポリモーフィック関連)が、本記事ではポリモーフィック関連を使うやり方について紹介する。

ポリモーフィック関連とは

Railsガイドにもあるように、本来モデルは1つのモデルにしか属することができないところを、複数のモデルに属することを可能にするものである。

ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できる。

https://railsguides.jp/association_basics.html#ポリモーフィック関連付け

要件

YouTubeのような動画プラットフォームの実装を想定。ユーザーは動画、コメントにいいねできる。いいねの対象は今後増える可能性がある(例:新しく音声投稿もできるようになり、それに対してもいいねを可能にしたいなど…)

ユーザーが1つの動画やコメントにいいねできるのは1回まで(user_idvideo_idは一意である)

何も考えずに実装

ユーザーは動画とコメントにいいねできる必要があるので、動画、コメントにそれぞれの専用いいねテーブルを生やす。

結果以下のようなテーブルが必要になる。

テーブル 説明
Video 動画テーブル
VideoLike 動画専用いいねテーブル
Comment コメントテーブル
CommentLike コメント専用いいねテーブル
User ユーザー

ER図

モデル

video.rb
class Video < ApplicationRecord
  has_many :video_likes
end
video_like.rb
class VideoLike < ApplicationRecord
  belongs_to :video
end
comment.rb
class Comment < ApplicationRecord
  has_many :comment_likes
end
comment_like.rb
class CommentLike < ApplicationRecord
  belongs_to :comment
end

生じ得るデメリット

  • いいねの対象のテーブル(VideoCommentVoice)が増えるたびに、専用のいいねテーブル(VideoLikeCommentLikeVoiceLike)も追加する必要があり追加コストが高い
  • 専用のいいねテーブルはいいねの対象が違うだけで性質はほとんど同じだが異なるテーブルとして存在するため、メンテナンス性が低下する
    • 例えばいいねの仕様が変わった時に全ての専用いいねテーブルに変更が発生

ポリモーフィック関連を用いた実装

VideoLikeCommentLikeを1つのLikeテーブルにまとめて、VideoCommentの両方に属することが可能な設計に変更する。

結果以下のようなテーブルが必要になる。

テーブル名 説明
Video 動画テーブル
Comment コメントテーブル
Like いいねテーブル(VideoとCommentに属することが可能)
User ユーザー

ER図

モデル

like.rb
class Like < ApplicationRecord
  belongs_to :likable, polymorphic: true
end
video.rb
class Video < ApplicationRecord
  has_many :like, as: :likable
end
comment.rb
class Comment < ApplicationRecord
  has_many :like, as: :likable
end

解決する課題

  • いいねの対象のテーブルが増えたとしても、モデルにhas_many :likes, as: :likableを追加するだけでよくなった
  • いいねの対象のテーブルごとにあった専用のいいねテーブルを1つのテーブルにまとめることができたため、メンテナンス性が向上

まとめ

複数のテーブルにいいねはポリモーフィック関連を使うことで、「いいねの対象の追加が容易になる」「メンテナンス性が向上する」のようなメリットがある。

参考

以下の資料が本記事を書くきっかけになりました。また内容もとても参考になりました。
ありがとうございます。

https://speakerdeck.com/pndcat/sen-luo-mo-xiang-ni-iine-surutamenodetagou-zao

Discussion