Thêm/sửa các điều kiện search cho Ransack
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 ...
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!