STI (単一テーブル継承)とポリモーフィズム関連を✅
絶賛ポートフォリオ悪戦苦闘中なわけで。
その中で、診断内容とお酒テーブルを結びつけたいと思っている。
そしてお酒テーブルから派生して同じカラムを持つ酒の種類テーブル?みたいなものを作りたい。
はいはい、中間テーブルにid持たせて多対多を作るやつね〜と思ったが、
どうやらSTIで関連付る方法があるそうで。
STIってなんですか状態だったので、学習していこうと思う。
初めの構想
enumを使ってこんな感じにすればお酒テーブルから派生して同じカラムを持つ酒の種類テーブルを作れる!と考えた。
class Alcohol < ApplicationRecord
enum alcohol_types: { beer: 0, shotyu: 1, nihonshu: 2, wine: 3, tyuhai: 4, wiskey: 5 }
しかしこうすると、それぞれのお酒にカラムを持たせることが出来ないと気づいた。
理想は下記のようにしたかった。
Alcoholモデル
#カラム名:型
name:string
description:text
alcohol_percentage:integer
alcohol_amount:integer
pure_alcohol_intake:integer
Beerモデル < Alcoholモデル
#カラム名:型
title:string
description:text
alcohol_percentage:integer
alcohol_amount:integer
pure_alcohol_intake:integer
上記のBeerモデルのように、Alcoholモデルの内容を継承したモデル(Beer,Wine,Shotyuなど)をたくさん作って酒ケジュールとして提供できる書き方を作りたかった。enumだと直感的にそれができなそう。そんな時に見つけたのが、**単一テーブル関連付け(STI)**だった。
STIとは
同じカラム設計のテーブルを、一つのテーブルにまとめて、継承することで余計なテーブルを増やさず、DRYなテーブル設計にするというもの。結びつけられたテーブル達は擬似テーブルであり、DBには実在しないテーブルになる。
お酒テーブルとビールやハイボールなどのテーブルを結びつけたい時。
通常だと、酒テーブルの内容を継承したハイボールテーブル、ビールモデル、サワーモデル、、と複数のテーブルを作る必要がある。
一方で単一テーブル継承という方法を使うと、「必要なテーブルは一つだけ」という状況を作り出すことができる。
どういうことか
例えばビールモデルを作るとすると、こうすることができる。
class Alcohol < ApplicationRecord
belongs_to :alcoholable, polymorphic: true
end
# インターフェースを明確化するために、moduleで固める
module Alcoholable
extend ActiveSupport::Concern
included do
has_many :alcohols, as: :alcoholable
end
def name
# オーバーライドされなかった場合はエラーが上がるようにしておく
raise NotImplementedError
end
def desctiption
raise NotImplementedError
end
def alcohol_percentage
raise NotImplementedError
end
def alcohol_amount
raise NotImplementedError
end
def pure_alcohol_intake
raise NotImplementedError
end
end
class Beer < ApplicationRecord
# Alcoholを使うポリモーフィックなモデルはAlcoholableをincludeする
include Alcoholable
# moduleで定義されているメソッドをオーバーライドする
def name
return "beer"
raise NotImplementedError
end
def desctiption
return "とりあえずビール"
raise NotImplementedError
end
def alcohol_percentage
return 5
raise NotImplementedError
end
def alcohol_amount
return 350
raise NotImplementedError
end
def pure_alcohol_intake
return 14
raise NotImplementedError
end
end
実在するテーブルは酒のみ。そのほかは擬似テーブルとなる。Beerモデルは、Alcoholableモジュールをincludeすることで、振る舞いを継承することが出来ている。
こうすることで、複数の同じ振る舞いをするモデルを簡単に作ることができる。
ポリモーフィック関連とどう違うの?
ポリモーフィック関連の特徴
ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。
一つのモデルを同じインターフェースを持ったものが扱う(ダックタイピングする)時に便利。
例えば下記のようにPictureモデルと関連づけたいテーブルが複数ある場合、Pictureモデルにableとつくカラムを用意し、関連づけたいテーブルはableがついたカラムをhas_manyすれば簡単に関連づけることができる。
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
ポリモーフィック関連を使って通知機能を実装(enum、既読管理なども) - Qiita
【Rails】単一テーブル継承(STI)について - Qiita
みんなRailsのSTIを誤解してないか!? - Qiita