Computer >> Máy Tính >  >> Lập trình >> Ruby

Tìm kiếm và lọc các mô hình đường ray mà không làm nghẽn bộ điều khiển của bạn

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:

app / models / product.rb
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:

app / controllers / product_controller.rb
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:

app / controllers / product_controller.rb
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:

ứng dụng / mô hình / mối quan tâm / filterable.rb
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
app / models / product.rb
class Product
  include Filterable
  ...
end
app / controllers / product_controller.rb
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 , locationstarts_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_locationfilter_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!