Scope trong Rails là gì và tại sao nó hữu ích?
Scope là các truy vấn tùy chỉnh bạn định nghĩa bên trong model Rails bằng phương thức scope.
Mỗi scope nhận hai đối số:
- Một tên, dùng để gọi scope này trong code.
- Một lambda, triển khai truy vấn.
Cú pháp trông như sau:
class Fruit < ApplicationRecord
scope :with_juice, -> { where("juice > 0") }
end
Kết quả khi gọi một scope là một đối tượng ActiveRecord::Relation.
Điều này có nghĩa là bạn có thể chuỗi (chain) và kết hợp các scope!
Ví dụ:
Fruit.with_juice.with_round_shape.first(3)
Khi Nào Nên Sử Dụng Scope?
Scope rất hay, nhưng khi nào nên dùng chúng? Xem ví dụ sau:
def index
@books = Book.where("LENGTH(title) > 20")
end
Đây là action index trong controller muốn hiển thị các sách có tiêu đề dài hơn 20 ký tự.
Code này chạy tốt. Nhưng nếu bạn muốn dùng truy vấn này ở nơi khác, bạn sẽ phải lặp lại code.
Code lặp lại khiến dự án khó bảo trì hơn. Hãy chuyển truy vấn này vào một scope.
Như sau:
class Book
scope :with_long_title, -> { where("LENGTH(title) > 20") }
end
Bây giờ action controller trông như sau:
def index @books = Book.with_long_title end
Gọn gàng hơn!
Cách Sử Dụng Scope Với Tham Số
Bạn có thể muốn đưa một biến vào scope để làm cho nó linh hoạt hơn.
Cách làm:
class Book
scope :with_long_title, ->(length) { where("LENGTH(title) > ?", length) }
end
Dấu hỏi (?) là placeholder, sẽ được thay thế bởi giá trị của length. Điều này làm cho code an toàn hơn (tránh SQL injection).
Nếu muốn giá trị mặc định:
class Book
scope :with_long_title, ->(length = 20) { where("LENGTH(title) > ?", length) }
end
Scope vs Phương Thức Lớp (Class Method)
Scope không làm gì phép màu cả. Chúng chỉ là các phương thức.
Thực tế... Bạn có thể làm điều tương tự bằng class method!
Ví dụ:
class Fruit
def self.with_juice
where("juice > 0")
end
end
Nhưng scope có ưu điểm thiết kế so với class method:
- Scope tạo ra code sạch hơn nhờ cú pháp riêng
- Scope chỉ dùng cho một mục đích duy nhất, nên bạn biết ngay mình nhận được gì khi thấy nó
- Scope không bị lẫn lộn với các phương thức khác, dễ dàng nhận diện hơn
Về mặt chức năng, khác biệt duy nhất là scope đảm bảo trả về ActiveRecord::Relation, còn class method thì không. Điều này giúp tránh lỗi khi scope không trả về kết quả.
Không Nên Dùng Default Scope
Default scope là scope được áp dụng tự động cho model.
Ví dụ:
class Post
default_scope { where(published: true) }
end
Default scope rất hấp dẫn. Nhưng thường là lựa chọn sai vì bạn dễ quên chúng đã được định nghĩa, dẫn đến lỗi kỳ lạ và tốn thời gian debug.
Nếu buộc phải làm việc với default scope, bạn có thể dùng phương thức unscoped để vô hiệu hóa tất cả scope đang áp dụng.
Tóm Tắt
Chúc mừng! Bạn đã học cách sử dụng Rails scope hiệu quả nhất. Đừng quên áp dụng kiến thức mới này vào thực tế để nhớ lâu. Cảm ơn bạn đã đọc! 🙂