Mọi người đều biết đến Active Record, một công cụ cho phép chúng ta viết các truy vấn dễ hiểu và có thể tái sử dụng. Hãy xem xét ví dụ sau với các mô hình liên quan đến sản phẩm và cửa hàng.
Mô tả các mô hình
- Cửa hàng (Shop): Lưu trữ thông tin về các cửa hàng nơi sản phẩm được bán.
- Sản phẩm (Product): Chứa thông tin chi tiết về sản phẩm, bao gồm giá tối đa, giá tối thiểu, tên, thương hiệu, danh mục và của hàng.
Yêu cầu hiển thị sản phẩm
Chúng ta cần xây dựng một trang web để hiển thị danh sách sản phẩm và có thể thêm một số bộ lọc như sau:
- Theo
shop_id
: chỉ hiển thị các sản phẩm thuộc một cửa hàng cụ thể. - Theo tên sản phẩm: nhập một chuỗi để tìm kiếm sản phẩm có chứa chuỗi đó.
- Theo giá: chọn sản phẩm có giá lớn hơn 100 đô la hoặc nhỏ hơn 150 đô la.
- Theo
brand_id
: lọc sản phẩm theo thương hiệu. - Theo
category_id
: lọc sản phẩm theo danh mục.
Ví dụ về truy vấn
Khi muốn lọc các sản phẩm, có thể sử dụng các câu lệnh sau:
ruby
query = Product.all
query = query.where(shop_id: params[:shop_id]) if params[:shop_id]
query = query.where("name LIKE ?", "%#{params[:name]}%") if params[:name]
query = query.where("min_price >= ?", params[:price_from]) if params[:price_from]
query = query.where("max_price <= ?", params[:price_to]) if params[:price_to]
query = query.where(brand_id: params[:brand_id]) if params[:brand_id]
query = query.where(category_id: params[:category_id]) if params[:category_id]
Tối ưu hóa truy vấn với lớp xây dựng
Phương pháp trên có thể giải quyết yêu cầu của chúng ta, nhưng trong thực tế, có thể có nhiều điều kiện và logic phức tạp hơn. Do đó, một lớp xây dựng truy vấn (Query Builder) có thể nhận một hash đầu vào và trả về kết quả mong muốn là một giải pháp hợp lý hơn. Dưới đây là cách xây dựng một lớp truy vấn cho sản phẩm:
ruby
class ProductQueryBuilder
attr_reader :params
FILTERS = %i(store_id brand_id category_id name from_price to_price)
def initialize(params = {})
@params = params
end
def exec
query = Product.all
FILTERS.each do |key|
next unless params[key]
query = query.where(send("#{key}_filter"), params[key])
end
query
end
def store_id_filter(store_id)
Product.arel_table[:store_id].eq(store_id)
end
def brand_id_filter(brand_id)
Product.arel_table[:brand_id].eq(brand_id)
end
def category_id_filter(category_id)
Product.arel_table[:category_id].eq(category_id)
end
def name_filter(name)
Product.arel_table[:name].matches("%#{sanitize_sql_like(name)}%")
end
def from_price_filter(price)
Product.arel_table[:min_price].gteq(price)
end
def to_price_filter(price)
Product.arel_table[:max_price].lteq(price)
end
end
Lời kết
Giờ đây, khi muốn sử dụng lớp ProductQueryBuilder
, chúng ta chỉ cần làm như sau:
ruby
params = {store_id: 1, brand_id: 3, category_id: nil}
ProductQueryBuilder.new(params).exec
Điều này giúp giảm thiểu mã nguồn và cải thiện khả năng bảo trì của ứng dụng. Bằng cách sử dụng lớp này, chúng ta có thể dễ dàng áp dụng các bộ lọc và xây dựng các truy vấn phức tạp hơn mà không cần phải viết nhiều câu lệnh where
mỗi lần.
Hy vọng rằng với bộ lọc truy vấn tiện lợi này, bạn sẽ có thể xây dựng các ứng dụng Rails mạnh mẽ hơn!
source: viblo