14
9

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 5 years have passed since last update.

RailsのSTIを使ってみた

Last updated at Posted at 2018-01-11

背景

既に運用中の求人サイトへ違う種類の求人を追加する要望が来た
現状の求人テーブルはjob_offersというテーブルがあり、中に既に数十万件データあり
job_offersとリレーションしてるテーブルが10個ぐらいがある

どう対応するか考える

別テーブルで追加するか既存のテーブルへ追加するか検討してみた結果
業務フローと修正コストなどの観点で既存のテーブルへ追加するすることにした

job_offersテーブル前後

STI(単一テーブル継承)を利用してテーブル設計

before

id content
1 既存求人

after

typeカラムを追加して、1の場合は既存求人
2,3は新規タイプの求人

id type content
1 1 既存求人タイプA
1 2 新規求人タイプB

対応詳細

カラム追加してmigrate

class AddTypeToJobOffers < ActiveRecord::Migration
  def change
    add_column :job_offers, :type, :integer, limit: 2, default: 1, null: false, after: :id
    add_index  :job_offers, :type
  end
end

新規タイプ求人クラス定義

class JobOffer
end

class TypeAJobOffer < JobOffer
end

class TypeBJobOffer < JobOffer
end

stiカラムをintegerにする

Railsのデフォルトではtypeをstringとして利用されて、中身クラス名を入れるようになってる
既存テーブルの件数が多くて、string利用する場合は遅いため、integerで対応するようにした

module JobOfferStiable
  extend ActiveSupport::Concern

  module ClassMethods
    def find_sti_class(type_name)
      case type_name
        when 1
          TypeAJobOffer
        when 2
          TypeBJobOffer
        else
          self
      end
    end

    def sti_name
      case self.to_s
        when 'TypeAJobOffer'
          1
        when 'TypeBJobOffer'
          2
        else
          0
      end
    end
  end
end
class JobOffer
  include JobOfferStiable
end

関連付け追加

顧客は複数の求人を持っている、TypeAとTypeBの求人関連付けを追加する

class Customer
    has_many :job_offers
    has_many :type_a_job_offers
    has_many :type_b_job_offers
end

テスト

# 全ての求人
JobOffer.count
sql: SELECT COUNT(*) FROM `job_offers`;

# TypeAの求人
TypeAJobOffer.count
sql: SELECT COUNT(*) FROM `job_offers` WHERE `job_offers`.`type` IN (1);

# TypeB求人
TypeBJobOffer.count
sql: SELECT COUNT(*) FROM `job_offers` WHERE `job_offers`.`type` IN (2);

# 顧客の全ての求人
Customer.last.job_offers

# 顧客の全てのTypeA求人
Customer.last.type_a_job_offers

# 顧客の全てのTypeB求人
Customer.last.type_b_job_offers

その他

開発中にSTIを無効にしたい

開発中に、他のブランチに切り替えして、STIテーブルを自動判定されるため、エラーになる
その場合は、STIを無効にすれば良い

def JobOffer
  self.inheritance_column = :_type_disabled
end

参考文書

http://ruby-rails.hatenadiary.com/entry/20141206/1417839458
https://github.com/viola/sti-example
https://qiita.com/taka0125/items/5a968817ff61aeb67107

14
9
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
14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?