注意
とにもかくにも最低限度の説明用です。何もわからない状態からの脱却を目指す用です。したがって、用語や説明が不十分なところもあるかと思います。また、例として挙げている設計(オブジェクト同士の関係)も現実のモデルとしてはふさわしくないところがあります。
ポリモーフィック関連とは何をするためのものか
「1対多(親と子)」の関係である2つのモデルがあるとします。
このとき、「子」のモデルと、現在紐付いている「親」とは別の「親」とを結びつける際に用いる手法です。「子」を2つ以上の複数の親に紐付けるときに使うことができる手法です。
適用させたいモデルの例
以下の2つのモデルがあると仮定します。説明の都合上、全く現実的ではないのですが、ここでは「一人の作者が書いた本は一つだけ(一人の作者は複数の本を書くことができない)」とさせて頂きます。そして以降も新しいモデルに対してはこの概念を前提とします。すなわち、「多対多」はあり得ないものとして扱わせて下さい。
- Book(本)
- 「親」
- Author(作者)
- 「子」
上記の関係は、Bookが「親」で、Authorが「子」です(一対多です)。
ここで、「Author」という「子」に、新たに「Music」という「親」を結びつけたいと思ったとします。「Author」に属している「作者」たちは、本も書けるし音楽も作れる人たちという設計にしたいとします。つまり、以下の2つの親子関係が作りたいことになります。
- Music(音楽)
- 「親」
- Author(作者)
- 「子」
ポリモーフィック関連を使わなくても紐付けはできる
上記の例を見てモデルを設計する場合、has_many
と belongs_to
を用いれば実現することができます。
「子」である Author のテーブルには book_id
および music_id
という、アソシエーションを担うカラムを追加します。そして、has_many
と belongs_to
を3つのモデル(Book、Music、Author)のファイルに適切に記述すればよいことになります。
ポリモーフィック関連を使う場合の方法
ポリモーフィック関連を使う場合には、上記の例では、以下のような方法(特徴)となります。
1. まず、ポリモーフィック関連の関連名を自由に命名する
親のモデル(BookとMusic)から、子のモデル(Author)をどのような名前でポリモーフィック関連として参照できるかの命名をまず行います。
Railsガイドや各種書籍では、HOGEable
のようにしている例が多いですが、任意の名前で構いません。
ここでは、nandemoii
という命名にします*1。
2. 命名した関連名についての2つのカラムを「子」のテーブルに追加する
命名した関連名について、以下の2つのカラムをAuthor(「子」)のテーブルに追加します。
nandemoii_type
- 型は
string
- このカラムには、「親」のモデル名の文字列が入ります
- 型は
nandemoii_id
- 型は
integer
- 型は
つまり、命名した関連名_type
と 命名した関連名_id
というカラムを「子」のテーブルに追加します。
この2つのカラムの作成は、以下のようにマイグレーションに書けば一気にやってくれます。
t.references :nandemoii, polymorphic: true, index: true
3. 子のモデルファイルに polymorphic: true オプションを付与した上で belongs_to を書く
Authorのモデルファイル*2に、以下のように書いてポリモーフィック関連を用いたアソシエーションを定義します。
class Author < ApplicationRecord belongs_to :nandemoii, polymorphic: true end
このとき、Authorのテーブル*3に、book_id
や music_id
などのカラムは不要です。ここが一つのポイントかと思います。
4. 親のモデルファイルに as: オプションを付与した上で has_many を書く
BookおよびMusicのモデルファイル*4に、アソシエーションを定義します。その際、as:
オプションを用いてポリモーフィック関連であることを明示します。as:
の値に来るのは先ほど命名した「関連名」です。
class Book < ApplicationRecord has_many :authors, as: :nandemoii end
class Music < ApplicationRecord has_many :authors, as: :nandemoii end
5. 動作を確認する
上記で設定ができたので、適当にレコードを追加して動作を確認してみます。具体的なカラムをこの例では作っていないのでオブジェクトの操作単位での記述になってしまいますが、以下のようにして確認すればいいでしょう。
5-1. 本(Book)を一冊作る
@book
という名前で本を一冊作ります。
5-2. 曲(Music)を一冊作る
@music
という名前で曲を一冊作ります。
5-3. @bookの作者(Author)を一人登録する
@book.authors.create(ほげほげ)
を実行することで、@bookの作者が一人登録されます。
5-4. @authorが書いた曲(Music)を一曲作る
@music.authors.create(ほげほげ)
を実行することで、@musicの作者が一人登録されます。
なおこのとき、「5-3」における@bookの作者と、この@musicの作者が概念的に同一であったとしても、別レコードで記録されます。
5-5. @bookからアソシエーションをたどってみる
@book(親)の方からAuthor(作者)を取得してみます。以下の命令を実行してみて期待通りの戻り値が得られるかどうかを確認します。Author(作者)は複数いる可能性があるのでauthors
メソッドを用います。
@book.authors
同様に、@music(親)からAuthor(作者)を取得してみます。Author(作者)は複数いる可能性があるのでauthors
メソッドを用います。
@music.authors
5-6. Authorの方からアソシエーションをたどってみる
Author(子)の方からBookおよびMusicを取得してみます。以下の命令を実行してみて期待通りの戻り値が得られるかどうかを確認します。Authorのレコードを2つ登録してあることが前提となっています。
Author.all[0].nandemoii
Author.all[1].nandemoii
Authorの各レコードに対して nandemoii
というポリモーフィック関連名をメソッドで使うことで、対応しているモデルのオブジェクトにアクセスできます。ここもポイントかと思います。
ポリモーフィック関連を使うと何が嬉しいのか
ポリモーフィック関連を使うと何が嬉しいのでしょうか。
今回の例の場合で考えると、Authorという「子」に、さらに別の「Art」という「親」を紐付けたいとなったとします。
この際、ポリモーフィック関連を使わないならば以下の作業が必要になります。
- Authorに
art_id
というカラムを追加する author.rb
にbelongs_to
を書くart.rb
にhas_many
を書く
一方、ポリモーフィック関連を使う場合は以下の作業が必要になります。
art.rb
にhas_many
を書く
これは嬉しいことの一つではないでしょうか。
参考
補足
あまり考えずに一気に書いてしまったので、徐々に遂行していきたいです。