LoginSignup
4
4

クラスの継承(単一テーブル継承)について【虎の巻】

Last updated at Posted at 2023-08-27

【概要】


作業時に、schemaの情報が定義されていないのにも関わらず、ActiveRecordでテーブルの情報を使用できることに疑問を感じ、調査してみると、単一テーブル継承と呼ばれる考え方が記されておりました。
下記に考え方や手順を備忘録として、残そうと思います。

環境


この記事は下記の実行環境で動作確認しております。
・Rails 7.0.4
・Ruby 3.1.4

【クラスの継承について】


まず、警鐘について確認したいと思います。

継承とはあるクラスを継承し、新たにクラスを作成することを指します。
┗例 Animalクラスを継承したdogクラスなど。(下記参考)

恩恵として、継承元の親クラス(スーパークラス)で定義された機能を子クラス(サブクラス)でも使用ができるようになります。

スクリーンショット 2023-08-27 20.55.59.png

【単一テーブル継承(STI)とは】


上記のような継承関係をRailsなどで扱う場合、各テーブルでカラム名が重複してしまいます。
上記の例で言うと、cryが重複していますね。
このような場合、役立つのが単一テーブル(以降STIと省略)になります。
STIを使用するとDBのテーブル1つに集約して定義することが可能になり、1つのテーブルでデータをまとめることができます!
これだけだとイメージがつかないと思いますので、少しずつ紐解いて考えていきましょう!

実装する前のイメージ


サンプルを1つ出して考えてみましょう。
ここで定義するテーブルのイメージを以下とします。

Image from Gyazo

ポイントは「taxonomies」テーブルです!
このテーブルでは、継承先「categories」「tags」「authors」を、継承元「taxonomies」で値を一括管理するということですね。これだけみると、普通にテーブルを作成するのと同じように見えますね。

では、なにが違うのでしょうか?実は下記の違いだけで実装ができてしまいます。

・データは全て「Taxonomies」で管理されるということ。
・そのほかのテーブルは実在はしないということ。

ここが通常のテーブル定義と異なるポイントとなります。
また、テーブル設計する際に、「tags」テーブルにしか持たない情報が欲しい場合、事前に継承元の「taxonomies」に定義するようにしましょう。

STIで実装する


上記までのイメージが掴めたら、継承元となる「Taxonomies」テーブルを作成し、DBの方に反映する作業を行いましょう。
下記がオペレーションの手順になります。

◇Taxonomiesテーブルを作成◇

rails generate migration CreateTaxonomies

◇内容を確認◇

<!-- xxxxxxxxxxxxxx_create_taxonomies.rb -->
class CreateTaxonomies < ActiveRecord::Migration[6.1]
  def change
    create_table :taxonomies, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" do |t|
      t.string :type #左記のようにカラムを編集
      t.string :name
      t.string :slug
      t.text :description
      t.datetime :created_at, null: false
      t.datetime :updated_at, null: false
    end

    add_index :taxonomies, :slug, unique: true
    add_index :taxonomies, :type
  end
end

◇マイグイレーションする◇

rails db:migrate

◇db/shema.rb◇

  create_table "taxonomies", charset: "utf8mb4", force: :cascade do |t|
    #force: :cascade ->親テーブルのレコードが削除された際に関連する子レコードも自動的に削除するオプション
    t.string "type"
    t.string "name"
    t.string "slug"
    t.text "description"
    t.datetime "created_at", precision: nil, null: false
    t.datetime "updated_at", precision: nil, null: false
    t.index ["slug"], name: "index_taxonomies_on_slug"
    t.index ["type"], name: "index_taxonomies_on_type"
  end

◇モデルに定義◇

<!-- app/models/tag.rb -->
class Tag < Taxonomy #継承関係の記述。
  has_many :article_tags
  has_many :articles, through: :article_tags
end
<!-- app/models/category.rb -->
class Category < Taxonomy #継承関係の記述。
  has_many :articles
end
<!-- app/models/author.rb -->
class Author < Taxonomy #継承関係の記述。
  has_many :articles
end

以上が実装となります。実際にテーブルの継承関係が問題ないか確認していきましょう!!

実際の挙動を確認


コンソール上から挙動を確認することができますので、確かめてみましょう。

rails console #コンソール起動

[1] pry(main)> Tag.first
  Tag Load (2.6ms)  SELECT `taxonomies`.* FROM `taxonomies` WHERE `taxonomies`.`type` = 'Tag' ORDER BY `taxonomies`.`id` ASC LIMIT 1
=> #<Tag:0x0000ffffb55824c8
 id: 1,
 type: "Tag",
 name: "a",
 slug: "a",
 description: nil,
 created_at: Thu, 17 Aug 2023 00:03:36.000000000 JST +09:00,
 updated_at: Thu, 17 Aug 2023 00:03:36.000000000 JST +09:00>

サンプルとして1件抽出してみました。
「Tag.first」と打鍵しているのに対して、SQLでは「taxonomies」から抽出されていますね。

SELECT `taxonomies`.* FROM `taxonomies`

上記のように継承元から継承先の情報を抽出できていれば、成功です!
継承元の「taxonomies」、継承先の「tag」テーブルを実際にあるように取得することができております。

【引用元】


・継承関係(動物)
https://medium-company.com/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0/
・Funna(ふんな)の技術ブログ
https://ryota21silva.hatenablog.com/entry/2020/06/09/184330
・[Rails] STI(単一テーブル継承)とメタプログラミングでDRY
https://qiita.com/kidach1/items/789c2e7aebbcfbd2583e

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4