Help us understand the problem. What is going on with this article?

RailsのSTIを使ってみた

More than 1 year has passed since last update.

背景

既に運用中の求人サイトへ違う種類の求人を追加する要望が来た
現状の求人テーブルは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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away