Thêm/sửa các điều kiện search cho Ransack
Bài đăng này đã không được cập nhật trong 7 năm
Chắc hẳn các bạn lập trình viên Ruby on Rails (RoR) đã không còn xa lạ gì với gem tìm kiếm nổi tiếng Ransack, tuy nhiên, đây là một gem khá linh hoạt và có nhiều tùy biến hay. Hôm nay tôi sẽ tổng hợp lại và giới thiệu đến các bạn một số cách tùy biến/ thêm các trường trong một ô tìm kiếm với Ransack.
Ransack
Đầu tiên tôi xin nói sơ qua về ransack( nếu bạn đã dùng và thông thạo bước cài đặt thì có thể bỏ qua bước này!) Cài đặt: Trong Gemfile:
gem "ransack'"
Hoặc
gem "ransack", github: "activerecord-hackery/ransack"
Sau đó chạy bundle install
Cách định nghĩa và thêm các trường cho một ô tìm kiếm
controllers
Ta định nghĩa một biến search cho params search của ransack:
@q = User.search(params[:q])
@users = @q.result.includes(:articles).page(params[:page]).to_a.uniq
views
Thông thường trong views mọi người sẽ kết hợp các trường search với tên các trường (muốn search) kết hợp luôn trên views
<%= search_form_for @q do |f| %>
<%= f.label :name_or_description_or_email_or_articles_title_cont %>
<%= f.search_field :name_or_description_or_email_or_articles_title_cont %>
<%= f.submit %>
<% end %>
Tuy nhiên, nếu muốn định nghĩa thêm trường search thì search_filed
kia sẽ là một chuỗi dài dằng dặc, một pha xử lý code khá "cồng kềnh".
Ransack Aliases
bằng cách sử dụng alias ta có thể rút gọn bớt đi các trường search trong views:
class Post < ActiveRecord::Base
belongs_to :author
# Abbreviate :author_first_name_or_author_last_name to :author
ransack_alias :author, :author_first_name_or_author_last_name
end
<%= search_form_for @q do |f| %>
<%= f.label :author_cont %>
<%= f.search_field :author_cont %>
<% end %>
bằng cách này cũng tiết kiệm được kha khá độ dài của cách đặt tên các trường search trong views.
Authorization (whitelisting/blacklisting)
Bằng cách định nghĩa whitelisting/blacklisting chúng ta có thể định nghĩa lại được các trường search của form từ trong model tương ứng với object thuộc class của model đó.
Theo mặc định, tât cả các columns của model được cho phép searching và sorting, nghĩa là các columns đều nằm trong whitelist, và không có class methods/scopes nào nằm trong whitelisted.
Ransack có thêm 4 medthods vào ActiveRecord::Base
để bạn có thể redefine lại các class methods trong model có thể được cho phép sorting hoặc searching, đó là:
ransackable_attributes
, ransackable_associations
, ransackable_scopes
và ransortable_attributes
.
Dưới đây là cách triển khai cho 4 methods để ta có thể override lại:
ransackable_attributes
# `ransackable_attributes` mặc định trả về tất cả các column_names
def ransackable_attributes(auth_object = nil)
column_names + _ransackers.keys
end
Ta có thể định nghĩa chỉ những trường nào mà ta muốn:
def ransackable_attributes(auth_object = nil)
%w(column_name1 column_name2 ...)
end
ransackable_associations
Dùng để định nghĩa (ghi đè) những trường có thể search từ association.
# `ransackable_associations` mặc định returns names
# của tất cả associations như là một array of strings.
# Để ghi đè một whitelist array of strings.
#
def ransackable_associations(auth_object = nil)
reflect_on_all_associations.map { |a| a.name.to_s }
end
Ta có thể định nghĩa lại các trường bên trong ransackable_associations
một cách tương tự như ransackable_attributes
bên trên.
ransortable_attributes
# mặc định trả về theo names
# tất cả các attributes có thể sắp xếp như một array strings
# dùng để override whitelist `ransortable_attributes` mặc định
def ransortable_attributes(auth_object = nil)
ransackable_attributes(auth_object)
end
Ta có thể định nghĩa lại các trường trong hàm ransortable_attributes
tương tự như ransackable_attributes
ở bên trên.
ransackable_scopes
Ta có thể thêm các scopes vào whitelist của các trường có thể search. Vì mặc định không có scope nào nằm trong whitelist, nên những scope được liệt kê ở đây đều là thêm vào :v
def ransackable_scopes(auth_object = nil)
%w(scope1, scope2 ...)
end
Using Scopes/Class Methods
Bên trên tôi đã nói qua về add các scopes vào whitelist có thể search cho ransack, dưới đây là một ví dụ cụ thể cho trường hợp add scopes vào whitelist như thế. Và công việc add class methods vào whitelist cũng có thể được làm tương tự.
class Employee < ActiveRecord::Base
scope :activated, ->(boolean = true) { where(active: boolean) }
scope :salary_gt, ->(amount) { where('salary > ?', amount) }
def self.hired_since(date)
where('start_date >= ?', date)
end
def self.ransackable_scopes(auth_object = nil)
if auth_object.try(:admin?)
# allow admin users access to all three methods
%i(activated hired_since salary_gt)
else
# allow other users to search on `activated` and `hired_since` only
%i(activated hired_since)
end
end
private_class_method :ransackable_scopes
end
Một lưu ý đối với scope/class methods có sử dụng query select
đó là:
Nếu kết hợp với gem "kaminari"
thì sẽ gặp vấn đề, bởi vì kaminari
có override lại query select
nên tác dụng của select
trong scopes/ class methods sẽ bị mất tác dụng (do bị override).
Thay vì dùng scopes/class methods thì bạn có thể dùng ransacker.
ransacker
Việc cài đặt bạn có thể xem tại ransacker để rõ hơn. Ở đây mình chỉ trình bày một số điểm khi tạo một ransacker. Ví dụ:
# in the model:
ransacker :created_at do
Arel.sql('date(created_at)')
end
- có thể gán câu truy vấn sql vào bên trong
Arel.sql
. - có thể truyền type cho kết quả của ransacker (mặc định sẽ là string)
ex:
ransacker :created_at, type: :date do # do anything end
Kết luận
Các vấn đề về ransack và ransacker khá rộng, trên đây chỉ là những tổng hợp của tôi về cách thêm/sửa các điều kiện, hay đúng hơn là các trường có thể search với ransack, trên cơ sở là vấn đề đã gặp trong công việc thực tế, và có tham khảo các tài liệu của ransack, ransacker. Bài viết còn có nhiều thiếu sót mong được các bạn đóng góp và góp ý thêm. Happy coding!
All rights reserved