シングルテーブル継承(単一テーブル継承、STI)とは
シングルテーブル継承(以下、STI)は、単一の継承階層に所属するクラス群を、ただひとつのテーブルを使って永続化する手法です。
言葉だけでは伝わリにくいので、下図を御覧ください。
Postsテーブル(スーパークラス)が、サブクラスに継承する形になります。
しかし、注意点が2点あります。
- Books,Clothes,Goodsテーブルは実際には存在しない。
- 個々のデータは全てスーパークラスのテーブルに保存されます。この場合はPostsテーブル。
それでは、STIはどういう場面で使えばいいのでしょうか。
STI継承しないと中間テーブルが再生産が起きる
私はSTIの存在を知らずにアプリを作っており、下図のようになってしまいました。
・・・。
PostsテーブルとしてはBooksとClothesとGoodsの記事を作りたかったのですが。。。
それぞれのテーブルを作ってしまうことで、それぞれの中間テーブル(ここでは、Comments,Likes,Picksテーブル)が再生産が起きてます。
機能自体はできましたが、ゴチャゴチャしすぎて保守的には最悪ですね・・・。
何より新機能をつける度に3つテーブルを用意する必要があり、骨が折れます😅
そこで!STIを使い、Postsクラスをサブクラスに継承させました!
以下の通りです。
めっちゃスッキリしましたね!
(画像にはBooks,Clothes,Goodsはテーブルとして書いてありませんが、継承させています。)
これで保守がしやすく、新機能もつけやすくなりました!
やり方(結論)
1. スーパークラスを作る
rails g model post
でマイグレーションファイルを作成し、テーブルの設定をします。
class CreatePosts < ActiveRecord::Migration[6.0]
def change
create_table :posts do |t|
t.references :user,foreign_key: true
#省略
t.string :type #typeカラムを作成します!
t.timestamps
end
end
end
ここで注意したいことは、typeカラムを作成することです。
この中に継承先のクラス名が入り、管理されます。
2. それぞれのクラスを作成し、スーパークラスを継承させる
先程のファイルをマイグレーションしてできたモデルファイルを編集したものです。
class Post < ApplicationRecord
belongs_to :user
#省略
end
それではrails g model book --parent=Post
でbookモデルを作成します。
--parent=PARENT
オプションを使うことで、マイグレーションファイルを生成せずに済みます。
class Book < Post
#こうすることでスーパークラスのアソシエーションやバリデーションが引き継がれます。
end
完成です!
ちなみに!
この状態でBookモデルを保存すると、Postsテーブルのtypeカラムに"Book"が代入された状態で,Postsテーブルに保存されます。
Book.create(title: "本です",.......)
以上でSTIのやり方は終わりです。
最後まで読んで頂きありがとうございました!
参考
Railsガイド
https://railsguides.jp/association_basics.html
[Rails] STI(単一テーブル継承)とメタプログラミングでDRY
https://qiita.com/kidach1/items/789c2e7aebbcfbd2583e