こんにちは!webエンジニアの奥村一貴(YOUTRUST / X)です。
入社してから二ヶ月が経ち、開発や業務フローにも少しずつ慣れてきました。
先日初めて「ポリモーフィック関連付け」という機能を知りました。
とても便利な機能だと感じたので、本日はYOUTRUSTでの利用方法と紐づけてポリモーフィック関連付けについてご紹介します。
ポリモーフィズム、ポリモーフィックとは
ポリモーフィズム
オブジェクト指向プログラミングにおいて、多様性や柔軟性を意味する概念です。
具体的には、異なるクラスを同様に扱えるようにし、同じメソッドを通じてそれらを操作できるようになります。
ポリモーフィック
ポリモーフィズムの概念に基づき、異なる型に柔軟に対応できる仕組みや特性のことを表します。
Railsのポリモーフィック関連付けは、この考え方をデータベースのリレーションやモデル間の関連付けに活かし、柔軟なデータ操作を可能にしています。
ポリモーフィックがないと何が困るのか
例えばCommentモデルと紐づく Post、Photoモデルがあるとします。
class Post < ApplicationRecord # Postに対するコメントの関連付け has_many :post_comments end class Photo < ApplicationRecord # Photoに対するコメントの関連付け has_many :photo_comments end class PostComment < ApplicationRecord # Postに紐づくコメント belongs_to :post end class PhotoComment < ApplicationRecord # Photoに紐づくコメント belongs_to :photo end
ポリモーフィックを利用しないと新たにコメント機能を実装したいリソースが増えるたびに、新しいコメントのテーブルを作成する必要があります。
その結果データベースの設計が複雑になったり、保守性が悪くなります。
Railsのポリモーフィック関連付け機能の紹介
ActiveRecordの機能の一つで以下のようなことができます。
データベースの設計において、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できる。
通常、モデル同士の関連付けは特定の2つのモデル間で定義されるが、ポリモーフィック関連付けを使うことで、1つのモデルがさまざまなモデルと動的に関連付けることが可能になる。
それではRailsにおけるポリモーフィック関連付けがどういった機能か具体的に紹介します。
使い方
モデルの定義
class Post < ApplicationRecord # コメントがつけられることを示す関連付け has_many :comments, as: :commentable end class Photo < ApplicationRecord # Photoにもコメントがつけられる has_many :comments, as: :commentable end class Comment < ApplicationRecord # commentableはPostやPhotoなど、異なるモデルに紐づけられる belongs_to :commentable, polymorphic: true end
データ作成
実際にデータを作成します。
上記で polymorphic
を使ってリレーションを作成することで、〇〇.comments.create
で関連するコメントを作成できます。
# Postに対してCommentを作成する post = Post.create(title: "初投稿!") comment = post.comments.create(content: "初投稿おめでとうございます!") # Photoに対してCommentを作成する photo = Photo.create(title: "写真を撮りました!") photo_comment = photo.comments.create(content: "とても綺麗です!")
データの取得方法
例えばとあるコメントが Post に紐付いているのか、Photo に紐づいているのか確認したい時などに、この polymorphic
はとても便利です。
comment = Comment.first comment.commentable # => PostオブジェクトまたはPhotoオブジェクト
モデルで設定した、commentable
メソッドを使うことで、コメントに紐づくPost または Photo のいずれかの関連モデルを取得することができます。
polymorphic
を使うことで、コメントの関連モデルを簡単に取得でき、複雑な外部キーのチェックなどのロジックが不要になります。
YOUTRUSTで実際に使用している機能
ここでは、YOUTRUSTで実際に使用しているポリモーフィック関連付けの具体例をご紹介します。
YOUTRUSTではホーム画面にいわゆるタイムラインのような機能があり、表示している要素の一つ一つをフィードアイテムと呼んでいます。
フィードに表示するコンテンツは「投稿」や「ユーザープロフィールの更新」などリソースの種類が多岐にわたります。
このフィードアイテムは、FeedItem
モデルとしてポリモーフィック関連付けを適用し、フィードに表示する様々なリソースとの紐付けを実現しています。
実際のソースコード (一部抜粋)
models/feed_item.rb
# Table name: feed_items # # id :bigint unsigned, not null, primary key # display_type :integer unsigned, not null # content_id :integer unsigned, not null # content_type :string(255) not null # created_at :datetime not null # updated_at :datetime not null # # Indexes # # i1 (content_id,content_type) UNIQUE # class FeedItem < ApplicationRecord belongs_to :content, polymorphic: true # 以下省略
このようにFeedItem
モデルには :content
というプロパティに対してpolymorphic
を設定しています。
content
というモデルは実際には存在しませんが、独自のモジュールを作成し、これをモデルにincludeすることで実現しています。
models/post.rb
class Post < ApplicationRecord # フィード表示用 include FeedContent # 以下省略
FeedContentモジュールでは以下の様なリレーションが定義されているので、feed_item
とpost
のリレーションができます。
has_one :feed_item, as: :content
以下はfeed_item
のデータ形式です。
紐付けたいモデルのレコードを作成後、そのレコードに対してmoduleのメソッドを実行することでFeedItem
モデルのレコードを作成します。
display_type
以下の様にFeedItem
モデルに定義してあるenumの値が入ります。(一部抜粋)
enum display_type: { post: 1, # 投稿 recruitment_post: 4, # 募集 community_post: 5, # コミュニティ投稿 }
content_id
関連するモデルのid。
例えばPost
モデルのフィードアイテムなら、その投稿のレコードのidが入る
content_type
クラス名が文字列で入る
このようにクラス名とidが格納されることにより、feed_item
のレコードを見るだけでどのモデルのどのレコードかが分かります。
呼び出し方
フィードアイテムのidさえ分かれば紐づいているさまざまなモデルのレコードを取得できます。
feed_item = FeedItem.find(1) puts feed_item.content => #<RecruitmentPost id: 1, # 以下省略
YOUTRUSTではこのような使い方で様々なリソースをタイムラインへ表示しています。
最後に
このような複雑なリレーションは最初は理解が難しかったですが、調べていくうちに非常に便利な機能であることがわかりました。
入社して2ヶ月が経ちましたが毎日新しい発見や学びがあり、できることが増えていくのを実感しています。
これからもさらなる成長を目指して頑張っていきます!
また、YOUTRUSTではさまざまなポジションでメンバーを募集しています。もしご興味があれば、ぜひお気軽にお話ししましょう! herp.careers