Rails のポリモーフィック関連を最低限度で理解をする

注意

とにもかくにも最低限度の説明用です。何もわからない状態からの脱却を目指す用です。したがって、用語や説明が不十分なところもあるかと思います。また、例として挙げている設計(オブジェクト同士の関係)も現実のモデルとしてはふさわしくないところがあります。

ポリモーフィック関連とは何をするためのものか

「1対多(親と子)」の関係である2つのモデルがあるとします。

このとき、「子」のモデルと、現在紐付いている「親」とは別の「親」とを結びつける際に用いる手法です。「子」を2つ以上の複数の親に紐付けるときに使うことができる手法です。

適用させたいモデルの例

以下の2つのモデルがあると仮定します。説明の都合上、全く現実的ではないのですが、ここでは「一人の作者が書いた本は一つだけ(一人の作者は複数の本を書くことができない)」とさせて頂きます。そして以降も新しいモデルに対してはこの概念を前提とします。すなわち、「多対多」はあり得ないものとして扱わせて下さい。

  • Book(本)
    • 「親」
  • Author(作者)
    • 「子」

上記の関係は、Bookが「親」で、Authorが「子」です(一対多です)。

ここで、「Author」という「子」に、新たに「Music」という「親」を結びつけたいと思ったとします。「Author」に属している「作者」たちは、本も書けるし音楽も作れる人たちという設計にしたいとします。つまり、以下の2つの親子関係が作りたいことになります。

  • Music(音楽)
    • 「親」
  • Author(作者)
    • 「子」

ポリモーフィック関連を使わなくても紐付けはできる

上記の例を見てモデルを設計する場合、has_manybelongs_to を用いれば実現することができます。

「子」である Author のテーブルには book_id および music_id という、アソシエーションを担うカラムを追加します。そして、has_manybelongs_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_idmusic_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.rbbelongs_to を書く
  • art.rbhas_many を書く

一方、ポリモーフィック関連を使う場合は以下の作業が必要になります。

  • art.rbhas_many を書く

これは嬉しいことの一つではないでしょうか。

参考

補足

あまり考えずに一気に書いてしまったので、徐々に遂行していきたいです。

*1:特徴的な命名にすることで、予約語でないことを明らかにしたり、視線に止まりやすくしたりするために、このような命名にしています

*2:author.rb

*3:authors

*4:book.rbおよびmusic.rb

Powered by はてなブログ