0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RailsのSTIで既存のカラム(外部キー)を使う

Last updated at Posted at 2020-05-03

RailsのSTIではtypeカラムにstring型でサブクラス名を保存する。
これは新しく0から設計する場合は良いかもしれない。
しかし既に次に述べるようなテーブルがある場合はintegerで関連付けられるほうが嬉しい。

Productテーブルが存在し、Product.category_idなどのカラムを使ってCategoryテーブルと紐付いているとする。
Product belongs_to Categoryの状態である。

product.rb
class Product < ApplicationRecord
  belongs_to :category, optional: true
end
category.rb
class Category < ApplicationRecord
  has_many :products
end

ここでCategoryごとにProductのサブクラスを作りたくなるとする。
今回の場合はCategoryがcpuやgpuなどである。

cpu.rb
class Cpu < Product
end

普通に新しく設計する場合、もしくは冗長性を許して新しくtypeカラムを作る場合は(typeカラムにcpuやgpuなどのstring型が保存され、category_idに1, 2などのint型が保存される)
単にtypeカラムを作ってあげるだけでよい。

今回は既存のcategory_idを利用してSTIを実現する。

product.rb
class Product < ApplicationRecord
  self.inheritance_column = 'category_id'
  belongs_to :category, optional: true
  class << self
    def find_sti_class(category_id)
      type_name = category_names[category_id - 1]
      "#{type_name.to_s.camelize}".constantize
    end

    def sti_name
      category_names.index(name.demodulize) + 1
    end
  end

  def self.category_names
    ['Cpu', 'Gpu']
  end
end

self.inheritance_columnにcategory_idを代入することで、今までrailsでtypeを用いてSTIを実現していたのが、category_idというカラムで実現できるようになる。
続いて子クラスの探索を行うActiveRecord::Inheritance(inheritance.rb)のfind_sti_classとsti_nameをオーバーライドする。
これにより①category_idというinteger型から必要なクラス名を取得することと②子クラス名からcategory_idを逆算することが可能になる。
実際にテストしてみると、

product = Product.where(category_id: 1).limit(1)[0] # cpu
p product.category_id # 1
p product.category # #<Category id: 1, name: "CPU">
p product.class.name # Cpu
gpu = Gpu.limit(1)[0] # #<Gpu id: *, name:...>

というように既存のリレーションを維持したまま、既存のカラムを利用してSTIが可能となった。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?