Best way to conditionally chain scopes

Since relations are chainable, it's often helpful to "build up" your search query. The exact pattern for doing that varies widely, and I'd caution against over-engineering anything, but using plain-old Ruby objects (POROs) to build up a query is common in most of the large Rails codebases I've worked in. In your case, you could probably get away with just simplifying your logic like so:

relation = Order.join(:store)

if params[:store_id]
  relation = relation.store(params[:store_id])
end

if params[:status]
  relation = relation.status(params[:status])
end

@orders = relation.all

Rails even provides ways to "undo" logic that has been chained previously, in case your needs get particularly complex.


You could do it like this:

query = Order
query = query.store(params[:store_id]) if params[:store_id].present?
query = query.status(params[:status]) if params[:status].present?
query = Order.joins(:store) if query == Order

Alternatively, you could also just restructure the status and store scopes to include the condition inside:

scope :by_status, -> status { where(status: status) if status.present? }

Then you can do this instead:

query = Order.store(params[:store_id]).by_status(params[:status])
query = Order.joins(:store) unless (params.keys & [:status, :store_id]).present?