1
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?

Rails `merge`で関連モデルの`scope`を再利用する

1
Posted at

背景

ActiveRecordに定義されていたscopemergeを用いた記述方法があり、これは便利だなと思ったので記録しておきます

モデルケース

基本的なECサイトのモデルケースです

order.rb
class Order < ApplicationRecord
  has_many :items
  
  # 支払い済みの注文
  scope :paid, -> { where(payment_status: 'paid') }
end
  • OrderItemを複数保持できる
  • Orderには、支払いステータスであるpayment_statusがあり、paidスコープが定義されている
item.rb
class Item < ApplicationRecord
  belongs_to :order
  
  # 在庫がある商品
  scope :in_stock, -> { where('quantity > ?', 0) }
end
  • Itemモデルにはquantityという在庫を管理するカラムがある
  • 在庫があるItemを取得するためにin_stockというスコープが定義されている

mergeの使い方

例えば、支払い済みかつ在庫がある商品を取得したい時はこんな感じにかけます

order.rb
class Order < ApplicationRecord
  has_many :items
  
  scope :paid, -> { where(payment_status: 'paid') }
  scope :ready_to_ship, -> { paid.joins(:item).merge(Item.in_stock) }
end

元々定義してあるpaidスコープでpaidステータスのデータを取りつつ、Itemの在庫があるデータです

なんでこの書き方が良いのか

当然、わざわざmerge使わなくていいじゃんとなります、例えば下記

order.rb
class Order < ApplicationRecord
  has_many :items
  
  scope :paid, -> { where(payment_status: 'paid') }
  scope :ready_to_ship, -> { paid.joins(:item).where('quantity > ?', 0) }
end

itemjoinして内部結合しているので可能です。

しかし、「在庫あり」という概念が変更になった場合はどうでしょうか?

例えば、「今現在の在庫はあるが、予約システムが導入されたので、予約済みの商品は配送できない」みたいなケース

item.rb
class Item < ApplicationRecord
  belongs_to :order
  
  # 在庫があり、予約されていない商品
  scope :in_stock, -> { where('quantity > ?', 0).where(reserved: false) }
end

この場合、Orderに記述したscopeも変更をする必要があります。同じ処理を2箇所で利用しているので

しかしscope :ready_to_ship, -> { paid.joins(:item).merge(Item.in_stock) }このように定義しておけば、変更の手間が省けて保守性が高くなります。

感想

  • 他モデルのスコープも使いまわせるの初めて知った
  • mergeの汎用性が高い

参考

1
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
1
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?