การค้นหา การจัดเรียง และการกรองในตัวควบคุม Rails อาจเป็นเรื่องยุ่งยาก ElasticSearch และ Solr เป็นโซลูชันที่มีประสิทธิภาพสูงและยอดเยี่ยม แต่เป็นการพึ่งพาอาศัยกันอย่างมากสำหรับแอปขนาดเล็ก
โชคดีที่ Rails มีขอบเขตซึ่งสามารถให้สิ่งที่คุณต้องการมากมายสำหรับการค้นหา การกรอง และการเรียงลำดับอย่างง่าย หากคุณใช้ประโยชน์จากการโยงขอบเขต คุณสามารถสร้างคุณลักษณะที่คุณต้องการโดยไม่ต้องพึ่งพาอาศัยกันจำนวนมากหรือเขียนโค้ดการค้นหาซ้ำๆ ด้วยตัวเอง
การค้นหาด้วยขอบเขต
ลองนึกภาพ #index
วิธีบนตัวควบคุม RESTful ของคุณที่แสดงตารางผลิตภัณฑ์ ผลิตภัณฑ์อาจใช้งานอยู่ รอดำเนินการ หรือไม่ใช้งาน มีอยู่ในที่เดียวและมีชื่อ
หากคุณต้องการกรองผลิตภัณฑ์เหล่านี้ คุณสามารถเขียนขอบเขตบางส่วน:
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
ขอบเขตแต่ละขอบเขตเหล่านี้กำหนดวิธีการเรียนในผลิตภัณฑ์ที่คุณสามารถใช้เพื่อจำกัดผลลัพธ์ที่คุณได้รับ
@products = Product.filter_by_status("active").filter_by_starts_with("Ruby") # => All active products whose names start with 'Ruby'
ผู้ควบคุมสามารถใช้ขอบเขตเหล่านี้เพื่อกรองผลลัพธ์ของคุณ:
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
และตอนนี้คุณแสดงเฉพาะผลิตภัณฑ์ที่ใช้งานอยู่ซึ่งมีชื่อขึ้นต้นด้วย "ทับทิม" ได้
https://example.com/products?status=active&starts_with=Ruby
แน่นอนว่าต้องมีการล้างข้อมูล
คุณสามารถดูว่ารหัสนี้เริ่มเทอะทะและซ้ำซากได้อย่างไร! แน่นอนว่าคุณกำลังใช้ Ruby อยู่ ดังนั้นคุณจึงสามารถวนซ้ำได้:
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
โซลูชันที่นำกลับมาใช้ใหม่ได้
คุณสามารถย้ายโค้ดนี้ไปยังโมดูลและรวมไว้ในโมเดลใดๆ ที่สนับสนุนการกรอง:
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
ตอนนี้คุณมีการกรองและค้นหาโมเดลของคุณด้วยบรรทัดเดียวในคอนโทรลเลอร์และหนึ่งบรรทัดในโมเดล ง่ายแค่ไหน? คุณยังสามารถรับการจัดเรียงในตัวโดยใช้ order
. ในตัว วิธีการเรียน แต่คุณควรเขียนขอบเขตของคุณเองสำหรับการเรียงลำดับ ด้วยวิธีนี้คุณสามารถตรวจสอบการป้อนข้อมูลอย่างมีสติ
เพื่อช่วยประหยัดแรงหน่อย ฉันใส่ Filterable
ลงในส่วนสำคัญ ลองใช้ในโครงการของคุณเอง มันช่วยฉันประหยัดเวลาและโค้ดได้มาก
อัปเดต: ขอบคุณ Jan Sandbrink ที่ชี้ให้เห็นบางอย่าง:ง่ายที่จะลืมเพิ่มพารามิเตอร์ใน filtering_params
. หากลืมไปก็สามารถเปิดแอปของคุณได้ถึงจริงจัง ปัญหาด้านความปลอดภัย
เพื่อหลีกเลี่ยงสิ่งเหล่านั้น แทนที่จะใช้ขอบเขตชื่อ status
, location
และ starts_with
, ฉันอัปเดตบทความนี้เป็นขอบเขตที่ตอนนี้ชื่อ filter_by_status
, filter_by_location
และ filter_by_starts_with
. วิธีนี้ชัดเจนและปลอดภัยยิ่งขึ้น
คำเตือนที่สำคัญ
การส่งพารามิเตอร์ไปยังขอบเขตเป็นวิธีที่ง่ายในการค้นหาและการกรองขั้นพื้นฐานในเว็บแอปของคุณ แต่ถ้าคุณไม่ระวังและยอมรับสิ่งที่ผู้ใช้ส่งถึงคุณ แอปของคุณอาจมีข้อบกพร่องด้านความปลอดภัยที่น่ารังเกียจ
โดยเฉพาะ order
มีความเสี่ยงต่อการฉีด SQL ดังนั้น หากคุณใช้ params เพื่อกำหนดลำดับการจัดเรียง คุณควร เสมอ ตรวจสอบชื่อคอลัมน์ที่ผู้ใช้ของคุณส่งถึงคุณ และอนุญาตเฉพาะค่าที่คุณรู้ว่าปลอดภัย
ไซต์ Rails SQL Injection จะช่วยให้คุณเรียนรู้ว่าวิธี ActiveRecord ใดมีช่องโหว่ คุณจึงสามารถรักษาแอปของคุณให้ปลอดภัยได้
คุณเรียนรู้ได้ดีขึ้นด้วยวิดีโอไหม
คุณสามารถดูทุกขั้นตอน ตั้งแต่เริ่มต้นแอปใหม่ไปจนถึงเพิ่มการค้นหาและการกรองใน screencast ที่แสดงร่วม เราจะสร้างแอป กรอกข้อมูลตัวอย่าง เพิ่มแบบฟอร์มค้นหา และเชื่อมต่อ และคุณจะได้รับแหล่งที่มาพร้อมกับวิดีโอ เพื่อให้คุณสามารถอ้างอิงกลับเมื่อคุณเพิ่มการค้นหาและการกรองแบบง่ายๆ ลงในแอป Rails ของคุณเอง
เรียนรู้เพิ่มเติมเกี่ยวกับ screencast ที่นี่!