Nên sử dụng Scope hay Class method?

Scope là một cách tuyệt vời để lấy các đối tượng đúng trong cơ sở dữ liệu của bạn. Ví dụ bạn có một scope như sau:

app/models/review.rb
class Review < ActiveRecord::Base
  scope :most_recent, -> (limit) { order("created_at desc").limit(limit) }
end

Bạn có thể sử dụng scope của mình như sau

app/models/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

Và gọi scope, trông giống hệt như gọi một class method của model Review. Trong khi đó thật dễ dàng để xây dựng nó như là một class method:

app/models/review.rb
def self.most_recent(limit)
  order("created_at desc").limit(limit)
end
app/controllers/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

Vậy câu hỏi đặt ra là tại sao phải dùng scope khi mà ta có thể dùng class method? hãy đến với phần tiếp theo

1. Why use scopes when we already have class methods?

Tiếp tục với vấ đề bên trên, bây giờ điều gì sẽ xảy ra nếu bạn muốn lấy tất cả các bài review được viết sau một thời gian cụ thể nào đó? và nếu không thời gian đó không được khai báo bạn phải lấy ra tất cả bài review. Với scope bạn có thể làm như thế này:

app/models/review.rb
scope :created_since, ->(time) { where("reviews.created_at > ?", time) if time.present? }

tương đối dễ dàng và ngắn gọn phải không nào, vậy với class method thì sao?

app/models/review.rb
def self.created_since(time)
  if time.present?
    where("reviews.created_at > ?", time)
  else
    all
  end
end

Và bạn phải viết dài hơn một chút so với scope. Ngoài ra Scopes thường return scopes, nên chúng dễ dàng nối với nhau(chained together) ví dụ:

Review.scope_1.scope_2.created_since(5.days.ago)

trong khi đó để làm cho class method làm việc theo cách tương tự, bạn phải xử lý trường hợp time = nil. Nếu không thì người gọi method sẽ phải xác định xem trường hợp đó có valid hay không. Nhưng method luôn trả về cùng một loại đối tượng lại thực sự hữu ích. Bạn không phải lo lắng nhiều về các trường hợp lỗi. Bạn có thể giả sử bạn sẽ luôn luôn được trao lại cho bạn một đối tượng bạn có thể sử dụng.

2. When should you use a class method instead of a scope?

Bởi vì các scope thể hiện mục đích của nó, nên tôi sử dụng chúng bất cứ khi nào tôi cần kết hợp các scope đơn giản, tích hợp sẵn (where và limit) thành các scope phức tạp hơn. Tìm đúng các tập đối tượng là những gì scope được thiết để làm. Có hai ngoại lệ:

  • Khi tôi cần phải preload scope, tôi chuyển chúng thành các associations để thay thế.
  • Khi tôi làm nhiều hơn chain built-in scopes thành các scope lớn hơn, tôi sử dụng các class method.

Khi logic của scope của bạn quá phức tạp, một class method là một sự lựa chọn đáng giá hơn. Ngoài ra bên trong một class method, bạn có thể dễ dàng mix mã Ruby với mã cơ sở dữ liệu. Nếu bạn có sorting code dễ viết hơn trong Ruby, bạn có thể lấy các đối tượng theo thứ tự mặc định của chúng và sử dụng sort_by để đặt chúng theo đúng thứ tự. Hoặc, nếu bạn muốn làm gì đó tricky, thì một class method có thể lấy dữ liệu từ một vài nơi khác nhau: cơ sở dữ liệu của bạn, Redis, hoặc một API hoặc dịch vụ bên ngoài. Sau đó, nó có thể tập hợp tất cả vào một bộ sưu tập của các object mà cảm thấy như một scope đó đã được chuyển thành một mảng. Tựu chung lại scope sinh ra để truy xuất ra các object còn class method có thể xử lý những logic phức tạp hơn và có thể lấy ra các giá trị trong database dễ dàng hơn. reference: http://www.justinweiss.com/articles/should-you-use-scopes-or-class-methods/