はじめに
皆さん、お疲れ様です。
今回、Railsの学習中に STI(Single Table Inheritance、シングルテーブル継承) について引っかかる部分がありましたので、備忘録として技術記事を作成しました。
もし同じく学習中の方でSTIの理解度について不安がある方への参考資料になればと思います...!
説明
この記事はプログラミング初学者なりに纏めた記事になっておりますので、内容等に誤り等がある場合がございますのでご了承下さい。
もし誤りに気づかれた際は、コメント等でご教授いただけると助かります🙇♂️
STIとは?
そもそも、STIとは何なのか?
その説明からしていきたいと思います。
STI(「Single Table Inheritance」または「シングルテーブル継承」)とは、オブジェクト指向プログラミングの継承の概念をデータベースのテーブルに適用したものです。
RailsでのSTIを使うと、複数の関連するクラスを単一のデータベーステーブルで扱うことが出来ます。
つまり、スーパークラス(親クラス)とそのサブクラス(子クラス)がデータを同じテーブルに保存し、区別するために一つのカラム(通常はtype
カラム)を使用する、という流れになります。
STIの例
例えば、交通手段を扱うアプリケーションを作っているとしましょう。
その中で、多くの共通の属性(color・brand・modele...etc)を持つ「Car」「Truck」「Motorcycle」といったクラスがあるとします。
これらのクラスは、「Vehicle」クラスから継承されているとします。
STIを用いる事で、「これらすべてのクラスに対して単一のvehicles
テーブルを使うことが出来る!」と言う事になります。
今回の疑問点
今回、既に用意されている《Ruby on Rails》を用いた記事投稿アプリ、その中の管理者専用の記事一覧画面に実装されている、セレクトボックスを用いた検索機能についてコードを読み解いていました。
ちなみに、検索機能については下記の通り実装されています。
- 記事の「カテゴリ」についての検索
- 記事の「著者」についての検索
- 記事の「タグ」
<%= form_with model: @search_articles_form, scope: :q, url: admin_articles_path, method: :get, html: { class: 'form-inline' } do |f| %>
<%= f.select :category_id, Category.pluck(:name, :id), { include_blank: true }, { class: 'form-control' } %>
<%= f.select :author_id, Author.pluck(:name, :id), { include_blank: true }, { class: 'form-control' } %>
<%= f.select :tag_id, Tag.pluck(:name, :id), { include_blank: true }, { class: 'form-control' } %>
<% end %>
上記コードの《Category.pluck(:name, :id)
》《Author.pluck(:name, :id)
》《Tag.pluck(:name, :id)
》部分は、plunk
メソッドを用いて、( )内に指定したカラムの値をモデルから配列として取得する内容となっています。
ただし、今回のアプリケーションはcategories
テーブル・authores
テーブル・tags
テーブルは存在しません。
その為、taxonomies
テーブルのtype
カラムに各レコードが表すサブクラスの名前「Category」「Author」「Tag」を保存しており、STIを用いる事でtype
カラムのそれぞれのレコードから値を取得出来るように実装しています。
実は、このコードを紐解いて調べている際、一つ疑問に感じた部分があります...。
それは...
なんで各モデルに対するテーブルを作成せず、単一のデータベーステーブルで扱うようにしているのだろう...
正直なところ、「各モデルのテーブルを作成して、それらでデータを管理する方が分かりやすくないか?」と疑問に感じてしまいました...笑
STIを使用しているメリット
ここからは、STIを使用するメリットについてお話ししていきたいと思います。
まず、STIを使用するメリットとしては以下の通りだと考えています。
1. データベースの単純化
STIを使うと、複数の関連するクラスのデータを1つのテーブルで管理することが出来るようになる。
これは、データベースの設計をシンプルにし、同時にテーブル間の関連を管理する複雑さを減らす事に繋がる。
例えば、「Car」「Truck」「Motorcycle」がある場合、これらを個別のテーブルとして管理するのではなく、一つのvehicles
テーブルで管理する事でテーブルの数が減り、データベースを管理しやすくなる。
2. クエリの単純化
関連する複数のタイプのオブジェクトを一つのテーブルに保持することで、データへのクエリが単純化する。
先ほどの例で説明すると...
特定の属性に基づいて全ての車両タイプを検索するとき、単一のテーブルを対象とするだけで良いので、JOIN操作などが不要になる。これにより、クエリの書き方が簡単になり、データを高速に取得する事が出来るようになる。
まとめ
通常、各クラスごとにテーブルを作る事になると、同じような構造のテーブルが複数できてしまって、メンテナンスや管理が大変になってしまいます...💦
そんな時にSTI(Single Table Inheritance)を使用して、複数の関連するモデルを単一のデータベーステーブルで管理出来るようにする事で、データの整合性を保ちつつ、コードのメンテナンスを効率的に行うことが出来るようになります...!
また、共通の属性やメソッドは親クラスに一度書くだけでOKなので、コードの重複が減り、よりDRY(Don't Repeat Yourself)な設計が可能になります。
慣れないうちはテーブルが存在しない事に困惑してしまうかもしれませんが、コードリーディングに慣れると仕組みとしてはそこまで複雑なものでは無いですね!