ポリモーフィック関連付けを理解する
背景
私は今までポリモーフィック関連付けというアソシエーションに出会うことなく、開発をしていました。現在関わっているRailsのプロダクトでは、ポリモーフィック関連付けを使って開発しているため、すぐに身につける必要がありました。
この記事では、それを知らない人へ伝えたいのと、その関連付けをたどる方法を紹介したいと思います。
使用する言語・フレームワーク
言語: Ruby3.2.2
フレームワーク: Rails7.0.4.3
ポリモーフィック関連とは
ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。より詳細な情報については、railsguidesにて確認してください。
コード例については、この記事専用のレポジトリを記載していますので、参考にしてください。
具体的な例
上記のRails Guideと同様に、Product / Picture / Emplyee のテーブルを追加しました。
アソシエーションも同様です。
この記事専用のレポジトリを作成しているため、コードの全体像を知りたい場合はをどうぞ。
ポリモーフィック関連を辿るために
初見で、Pictureモデルのファイルを見たときに、どのテーブルとアソシエーションを持っているかは、これだけでは分かりません。
このアソシエーション先を把握するためには、自分が知っている方法は3つです。
1. as: imagetableで、全文検索をかける。
grep "as: :imagetable"
とターミナルで打って、ヒットしたモデルがアソシエーションとして、分かるパターン
2. DBのレコードから判断する
Pictureテーブルから、レコードを全件取得して、カラムimagetable_idで重複ないようにすると、アソシエーション先のテーブルを知ることができます。しかし、この方法は、リスクがあります。
例えば、今日あなたは新しい会社へ入社したとします。その会社のプロダクトの開発環境用や本番環境用のDBにあるPictureテーブルにあるレコードだけを見て、Pcitureテーブルが、アソシエーション先に関連するレコードを全て持っているかどうかを判断できますでしょうか?
それは不可能だと思います。
また、seed.rbでアソシエーション先のレコードを保存するように定義している会社さんもいるかと思いますが、それはそれでエンジニアさんが手作業でコーティングしているため、抜け漏れがあるかもしれません。
これらの可能性があるため、テーブルのレコードからアソシエーション先を把握することはおすすめしません。しいて言うならば、カラムに入る値にどんなデータが入るのかを確認する程度、いいかと思います。
3. Erd Gemを利用して、アソシエーションを把握する
Erd Gemというテーブル間のアソシエーションをブラウザ上で閲覧できるGemがあります。
この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についての記事をアウトプットしていければと思います。
参考記事
Discussion