Ruby on Railsで関連するモデルをまとめる仕組みとして、STIとPolymorphicがあります。 この記事では、それぞれの概要や適するケース、実装方法について解説します。
概要
STI
STI(Single Table Inheritance)は、単一のテーブルを継承する仕組みのことをいいます。 つまり、複数のモデルで1つのテーブルを共有するのがSTIです。
複数のモデルにおいて、構造は同じだがふるまいが異なるというケースに適しています。
たとえば、一般ユーザ(Member)と管理者(Admin)を単一のusersテーブルで管理する、などが考えられます。
Polymorphic
Polymorphicは、あるモデルを複数のモデルに属させる仕組みのことをいいます。
特定のふるまいを複数のモデルに持たせるケースに適しています。
たとえば、コメント(Comment)というふるまいを記事(Article)や掲示板(Board)に持たせる、などが考えられます。
実装方法
STI
STIの実装手順は2ステップで、まず(1)継承元モデルのテーブルにtypeカラムを追加し、あとは必要に応じて(2)継承先モデルを作成します。
まず、Userモデルと、それを永続化するusersテーブルを作成します。
テーブルにはtypeカラムを追加します。
$ bin/rails g model User type:string
$ bin/rake db:migrate
次に、Userモデルを継承するモデルを作成します。
ここでは異なるふるまいを持たせる例として、can?メソッドを定義しています。
class Member < User
def can?
false
end
end
class Admin < User
def can?
true
end
end
コンソールで確認してみます。
member = Member.create
member.class #=> Member
member.type #=> "Member"
member.can? #=> false
Polymorphic
Polymorphicの実装手順は、まず(1)ふるまいを定義するモデルにxxx_typeとxxx_idという2つのカラムを追加します。
次に、(2)そのモデルと、それを属させるモデルに対して関連を定義します。
まず、Comment、Article、Boardモデルを作成します。
commentsテーブルには必要なカラムを追加します。
$ bin/rails g model Comment commentable_resource_type:string commentable_resource_id:integer
$ bin/rails g model Article
$ bin/rails g model Board
$ bin/rake db:migrate
次に、各モデルに対して関連を定義します。
class Comment < ActiveRecord::Base
belongs_to :commentable_resource, polymorphic: true
end
class Article < ActiveRecord::Base
has_many :comments, as: :commentable_resource
end
class Board < ActiveRecord::Base
has_many :comments, as: :commentable_resource
end
カラム名や関連名は任意のものでよいですが、これらは一致させる必要があります。
つまり、カラム名がxxx_typeやxxx_idの場合、関連はbelongs_to :xxxとなります。
コンソールで確認してみます。
article = Article.create
comment = article.comments.create
comment.commentable_resource.class #=> Article
comment.commentable_resource.id #=> 1
おわりに
STIとPolymorphicの仕組みや適するケースを理解しておくと、同じようなケースが生じた際にきれいに設計できると思います。 頭の片隅にでもとどめておくとよいかもしれません。