Cả scope và class method đều có thể trả ActiveRecord::Relation:
1 | class Product < ApplicationRecord |
Khác biệt thực dụng
Scope luôn được kỳ vọng composable như relation. Nếu block trả nil/false, Rails có behavior fallback về relation hiện tại, điều này có thể gây bất ngờ:
1 | scope :by_type, ->(type) { where(product_type: type) if type.present? } |
Khi type blank, query có thể trở thành “không filter” thay vì fail. Viết rõ:
1 | scope :by_type, ->(type) { |
Class method linh hoạt hơn cho branching, guard clause và nhiều bước. Nếu trả relation, nó vẫn chain được:
1 | Product.active.by_type(params[:type]).order(:name) |
Khi nào dùng?
- Query ngắn, declarative, tái sử dụng → scope.
- Logic nhiều nhánh/tham số → class method.
- Domain query lớn → query object.
Không đặt query evaluation như .to_a, .first vào giữa chain nếu muốn tiếp tục compose.
default_scope
Cẩn thận với default_scope; nó tác động ngầm lên mọi query và update. Multi-tenancy/security không nên chỉ dựa vào một default scope dễ unscoped.
Scope không tốt hơn class method vì ngắn hơn. Giá trị thật là trả relation có contract rõ. DSL đẹp nhưng trả dữ liệu bất ngờ vẫn chỉ là bug mặc vest.