👀

ポリモーフィック関連付けを理解する

2023/05/22に公開

背景

私は今までポリモーフィック関連付けというアソシエーションに出会うことなく、開発をしていました。現在関わっているRailsのプロダクトでは、ポリモーフィック関連付けを使って開発しているため、すぐに身につける必要がありました。
この記事では、それを知らない人へ伝えたいのと、その関連付けをたどる方法を紹介したいと思います。

使用する言語・フレームワーク

言語: Ruby3.2.2
フレームワーク: Rails7.0.4.3

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

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

ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。より詳細な情報については、railsguidesにて確認してください。
コード例については、この記事専用のレポジトリを記載していますので、参考にしてください。

具体的な例

上記のRails Guideと同様に、Product / Picture / Emplyee のテーブルを追加しました。
アソシエーションも同様です。
この記事専用のレポジトリを作成しているため、コードの全体像を知りたい場合はをどうぞ。
https://github.com/sontixyou/rails_polymorphic_association_practice

ポリモーフィック関連を辿るために

初見で、Pictureモデルのファイルを見たときに、どのテーブルとアソシエーションを持っているかは、これだけでは分かりません。

このアソシエーション先を把握するためには、自分が知っている方法は3つです。

1. as: imagetableで、全文検索をかける。

grep "as: :imagetable"とターミナルで打って、ヒットしたモデルがアソシエーションとして、分かるパターン

2. DBのレコードから判断する

Pictureテーブルから、レコードを全件取得して、カラムimagetable_idで重複ないようにすると、アソシエーション先のテーブルを知ることができます。しかし、この方法は、リスクがあります。

例えば、今日あなたは新しい会社へ入社したとします。その会社のプロダクトの開発環境用や本番環境用のDBにあるPictureテーブルにあるレコードだけを見て、Pcitureテーブルが、アソシエーション先に関連するレコードを全て持っているかどうかを判断できますでしょうか?

それは不可能だと思います。

また、seed.rbでアソシエーション先のレコードを保存するように定義している会社さんもいるかと思いますが、それはそれでエンジニアさんが手作業でコーティングしているため、抜け漏れがあるかもしれません。

これらの可能性があるため、テーブルのレコードからアソシエーション先を把握することはおすすめしません。しいて言うならば、カラムに入る値にどんなデータが入るのかを確認する程度、いいかと思います。

3. Erd Gemを利用して、アソシエーションを把握する

Erd Gemというテーブル間のアソシエーションをブラウザ上で閲覧できるGemがあります。
https://github.com/amatsuda/erd

このGemを利用すると、以下のようにテーブル間のアソシエーションを確認することができます。

URLは、http://localhost:3000/erdで確認することができます。

レコードの中身はどうなっているのか

Pictureモデルのimageable_typeにはアソシエーション先のモデル名であるProductとimageable_idにはProductのIDが入っています。

> product = Product.first
  Product Load (0.3ms)  SELECT "products".* FROM "products" ORDER BY "products"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Product:0x0000000107ef3ca8 id: 4, title: "Rails app0", created_at: Sun, 21 May 2023 05:19:32.852893000 UTC +00:00, updated_at: Sun, 21 May 2023 05:19:32.852893000 UTC +00:00>
irb(main):006:0> product.pictures.first
  Picture Load (0.3ms)  SELECT "pictures".* FROM "pictures" WHERE "pictures"."imageable_id" = ? AND "pictures"."imageable_type" = ? ORDER BY "pictures"."id" ASC LIMIT ?  [["imageable_id", 4], ["imageable_type", "Product"], ["LIMIT", 1]]
=>
#<Picture:0x000000011013e618
 id: 4,
 title: "Rails app0",
 body: "Rails app0's body",
 imageable_type: "Product",
 imageable_id: 4,
 created_at: Sun, 21 May 2023 05:19:32.856791000 UTC +00:00,
 updated_at: Sun, 21 May 2023 05:19:32.856791000 UTC +00:00>

Pictureモデルのimageable_typeにはアソシエーション先のモデル名であるEmployeeとimageable_idにはEmployeeのIDが入っています。

> employee = Employee.first
  Employee Load (0.1ms)  SELECT "employees".* FROM "employees" ORDER BY "employees"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Employee:0x0000000110634168 id: 2, first_name: "taro", family_name: "rails", created_at: Sun, 21 May 2023 05:19:32.840650000 UTC +00:00, updated_at: Sun, 21 May 2023 05:19:32.840650000 UTC +00:00>
irb(main):008:0> employee.pictures.first
  Picture Load (0.2ms)  SELECT "pictures".* FROM "pictures" WHERE "pictures"."imageable_id" = ? AND "pictures"."imageable_type" = ? ORDER BY "pictures"."id" ASC LIMIT ?  [["imageable_id", 2], ["imageable_type", "Employee"], ["LIMIT", 1]]
=>
#<Picture:0x0000000107ef51e8
 id: 2,
 title: "company",
 body: "company's body",
 imageable_type: "Employee",
 imageable_id: 2,
 created_at: Sun, 21 May 2023 05:19:32.847956000 UTC +00:00,
 updated_at: Sun, 21 May 2023 05:19:32.847956000 UTC +00:00>

最後に

ポリモーフィック関連付けを初めて見たときには、その関連付けの設定方法やアソシエーションを把握しづらかったのですが、自分でコードを書いてみると理解が深まります。今後、RailsやRubyについての記事をアウトプットしていければと思います。

参考記事

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

https://docs.gitlab.com/ee/development/database/polymorphic_associations.html

Discussion