Tìm kiếm, sắp xếp và lọc trong bộ điều khiển Rails có thể là một vấn đề khó khăn. ElasticSearch và Solr là những giải pháp tuyệt vời, được hỗ trợ cao, nhưng lại là những giải pháp phụ thuộc thực sự lớn đối với một ứng dụng nhỏ.
May mắn thay, Rails bao gồm các phạm vi, có thể cung cấp cho bạn rất nhiều thứ bạn cần để tìm kiếm, lọc và sắp xếp đơn giản. Nếu bạn tận dụng chuỗi phạm vi, bạn có thể xây dựng các tính năng bạn muốn mà không cần phụ thuộc quá nhiều hoặc tự viết một loạt mã tìm kiếm lặp đi lặp lại.
Tìm kiếm với phạm vi
Hãy tưởng tượng một #index
trên bộ điều khiển RESTful của bạn hiển thị một bảng các sản phẩm. Các sản phẩm có thể đang hoạt động, đang chờ xử lý hoặc không hoạt động, có sẵn ở một địa điểm và có tên.
Nếu bạn muốn có thể lọc các sản phẩm này, bạn có thể viết một số phạm vi:
class Product < ActiveRecord::Base
scope :filter_by_status, -> (status) { where status: status }
scope :filter_by_location, -> (location_id) { where location_id: location_id }
scope :filter_by_starts_with, -> (name) { where("name like ?", "#{name}%")}
end
Mỗi phạm vi trong số này xác định một phương thức lớp trên Sản phẩm mà bạn có thể sử dụng để giới hạn kết quả bạn nhận lại.
@products = Product.filter_by_status("active").filter_by_starts_with("Ruby") # => All active products whose names start with 'Ruby'
Bộ điều khiển của bạn có thể sử dụng các phạm vi này để lọc kết quả của bạn:
def index
@products = Product.where(nil) # creates an anonymous scope
@products = @products.filter_by_status(params[:status]) if params[:status].present?
@products = @products.filter_by_location(params[:location]) if params[:location].present?
@products = @products.filter_by_starts_with(params[:starts_with]) if params[:starts_with].present?
end
Và bây giờ bạn có thể chỉ hiển thị các sản phẩm đang hoạt động có tên bắt đầu bằng ‘Ruby’.
https://example.com/products?status=active&starts_with=Ruby
Rõ ràng, điều này cần được dọn dẹp một chút
Bạn có thể thấy mã này bắt đầu khó sử dụng và lặp đi lặp lại như thế nào! Tất nhiên, bạn đang sử dụng Ruby, vì vậy bạn có thể nhồi nhét cái này vào một vòng lặp:
def index
@products = Product.where(nil)
filtering_params(params).each do |key, value|
@products = @products.public_send("filter_by_#{key}", value) if value.present?
end
end
private
# A list of the param names that can be used for filtering the Product list
def filtering_params(params)
params.slice(:status, :location, :starts_with)
end
Một giải pháp có thể tái sử dụng nhiều hơn
Bạn có thể di chuyển mã này vào một mô-đun và đưa nó vào bất kỳ mô hình nào hỗ trợ lọc:
module Filterable
extend ActiveSupport::Concern
module ClassMethods
def filter(filtering_params)
results = self.where(nil)
filtering_params.each do |key, value|
results = results.public_send("filter_by_#{key}", value) if value.present?
end
results
end
end
end
class Product
include Filterable
...
end
def index
@products = Product.filter(params.slice(:status, :location, :starts_with))
end
Giờ đây, bạn có thể lọc và tìm kiếm các mô hình của mình với một dòng trong bộ điều khiển và một dòng trong mô hình. Điều đó dễ dàng như thế nào? Bạn cũng có thể có được tính năng sắp xếp tích hợp bằng cách sử dụng order
được tích hợp sẵn phương thức lớp, nhưng có lẽ tốt hơn là bạn nên viết các phạm vi của riêng bạn để sắp xếp. Bằng cách đó, bạn có thể kiểm tra thông tin đầu vào một cách tỉnh táo.
Để giúp bạn tiết kiệm một chút công sức, tôi đặt Filterable
thành ý chính. Hãy thử trong dự án của riêng bạn, Điều đó đã giúp tôi tiết kiệm rất nhiều thời gian và mã.
Cập nhật: Cảm ơn Jan Sandbrink đã chỉ ra điều gì đó:Rất dễ quên đưa các thông số vào danh sách trắng trong filtering_params
. Nếu bạn quên, nó có thể mở ứng dụng của bạn lên đến nghiêm trọng vấn đề bảo mật.
Để tránh tất cả những điều đó, thay vì sử dụng phạm vi có tên status
, location
và starts_with
, Tôi đã cập nhật bài viết này lên các phạm vi đó hiện được đặt tên là filter_by_status
, filter_by_location
và filter_by_starts_with
. Theo cách đó, chúng rõ ràng hơn và an toàn hơn.
Một cảnh báo quan trọng
Gửi thông số đến phạm vi là một cách dễ dàng để tìm kiếm và lọc cơ bản trong ứng dụng web của bạn. Nhưng nếu bạn không cẩn thận và chấp nhận bất cứ thứ gì người dùng gửi cho bạn, ứng dụng của bạn có thể có một số lỗi bảo mật khá khó chịu.
Đặc biệt, order
dễ bị tấn công bởi SQL injection. Vì vậy, nếu bạn đang sử dụng các tham số để xác định thứ tự sắp xếp của mình, bạn nên luôn luôn kiểm tra tên cột mà người dùng của bạn đang gửi cho bạn và chỉ cho phép các giá trị bạn biết là an toàn.
Trang web Rails SQL Injection sẽ giúp bạn tìm hiểu về các phương thức ActiveRecord nào dễ bị tấn công, vì vậy bạn có thể giữ an toàn cho ứng dụng của mình.
Bạn có học tốt hơn với video không?
Bạn có thể xem từng bước, từ khởi động một ứng dụng hoàn toàn mới đến thêm tìm kiếm và lọc, trong video truyền hình đi kèm. Chúng tôi sẽ tạo một ứng dụng, điền vào nó với dữ liệu mẫu, thêm một biểu mẫu tìm kiếm và kết nối nó. Và bạn sẽ nhận được nguồn cùng với các video, vì vậy bạn có thể tham khảo lại khi bạn thêm tính năng tìm kiếm và lọc đơn giản vào ứng dụng Rails của riêng mình.
Tìm hiểu thêm về video màn hình tại đây!