Sử dụng Ransack trong search và sort rails

Giới thiệu

Ransack là một gem mạnh mẽ và giàu tính năng được sử dụng rộng rãi để triển khai khả năng tìm kiếm nâng cao trong ứng dụng Ruby on Rails.

Ransack cho phép tạo cả các biểu mẫu tìm kiếm đơn giản và nâng cao cho ứng dụng Ruby on Rails của bạn. Nó cung cấp các chức năng tìm kiếm, lọc và sắp xếp trong trang web. Bạn có thể tạo các hình thức tìm kiếm đơn giản cũng như nâng cao với loại gem này.

Cài đặt

Hãy để cùng nhau xây dựng một ứng dụng tìm kiếm nhỏ bằng các tùy chọn tìm kiếm đơn giản và nâng cao.

Trước hết, thêm gem Ransack vào file Gemfile của bạn: gem 'ransack'

Sau đó, bạn chạy lệnh: bundle install

Trước tiên, hãy tạo ra một số bảng chúng ta có thể sử dụng thêm để khám phá các tùy chọn tìm kiếm khác nhau do Ransack cung cấp.

Ta tạo 2 bảng: students và info-sudents

rails g migration Students username:string email:string
rails g migration InfoStudents birth_date:string area:integer major:integer course:integer phone:string user_id:integer

Sau đó, bạn tạo thêm dữ liệu cho 2 bảng để phục vụ cho việc tiềm kiếm.

Ứng dụng

Giờ thì ta sẽ đi ứng dụng ransack vào việc tìm kiếm. Ở đây, ta sẽ list danh sách của bảng students và thực hiện tìm kiếm ở 2 bảng students và info_students

Trong controller, ta tạo file StudentsController.rb

  def index
    @query = Student.joins(:info_student).includes(:info_student).ransack(search_params)
    @students = @query.result.page(params[:page])

    return unless params[:q].present? && @students.blank?
  end

  private

  def search_params
    list_info_student = (handle_key_search "username_cont").split.partition{|x| x.include?("@")}
    {
      email_case_insensitive_cont_any: list_info_student[0].map(&:downcase),
      username_case_insensitive_cont_any: list_info_student[1].map(&:downcase),
      age_lteq: handle_key_search("age_lteq"),
      age_gteq: handle_key_search("age_gteq"),
      area_eq: handle_key_search("area_eq"),
      major_eq: handle_key_search("major_eq"),
      course_eq: handle_key_search("course_eq"),
      phone_eq: handle_key_search("phone_eq"),
    }
  end
  
  def handle_key_search key
    params[:q].try(:[], key) || ""
  end
end

Ở đây, @q là một đối tượng ransack :: search sử dụng tham số có trong params [: q].

Và phương thức ransack #result trả về một đối tượng ActiveRecordRelation với kết quả phù hợp mà chúng ta có thể sử dụng để hiển thị ra view.

Ransack cung cấp các kết quả tìm kiếm khác nhau như dưới đây. Chúng ta có thể áp dụng bất kỳ loại nào dựa trên yêu cầu của tìm kiếm.

Dưới đây là danh sách tất cả matches có sẵn mà bạn có thể dùng theo yêu cầu.

Tiếp theo, trong model student.rb, ta sẽ thực hiện tùy chỉnh việc tìm kiếm:

// để search với TH case_insensitive ta thực hiện như sau:
 ransacker :username_case_insensitive, type: :string do
    arel_table[:username].lower
  end

  ransacker :email_case_insensitive, type: :string do
    arel_table[:email].lower
  end

// để search với tuổi
  ransacker :age, type: :integer do
    Arel.sql("FLOOR(TIMESTAMPDIFF(DAY, info_students.birth_date, CURDATE())/365)")
  end

//search với cá bảng liên kết
  ransacker :area, type: :integer do
    Arel.sql("info_students.area")
  end

  ransacker :major, type: :integer do
    Arel.sql("info_students.major")
  end

  ransacker :course, type: :integer do
    Arel.sql("info_students.course")
  end
  
  ransacker :phone, type: :string do
    Arel.sql("info_students.phone")
  end

Ở đây, ta sử dụng phương thức ransacker, nó cho phép tùy chỉnh tìm kiếm và được đặt trong model.

Ransackers, giống như scopes, không phải là một cure-all. Nhiều trường hợp sử dụng có thể được giải quyết tốt hơn với tìm kiếm Ransack tiêu chuẩn.

Cuối cùng, ta hiển thị search ra view. Trong file index.html

<%= search_form_for @query, url:students_path do |f| %>
  <%= f.search_field :username_cont, class: "form-control",
    value: handle_key_search("username_cont") %>
  <div>
    <%= f.label :age %>
    <div>
      <%= f.number_field :age_gteq, class: "form-control",
        value: handle_key_search("age_gteq") %>
      <p>〜</p>
      <%= f.number_field :age_lteq, class: "form-control",
        value: handle_key_search("age_lteq") %>
    </div>
  </div>
  <div>
    <%= f.label :area %>
    <div>
      <%= f.select :area_eq, options_for_select(@options[:area], selected: handle_key_search("area_eq")),
        {include_blank: true} %>
    </div>
  </div>
  <div>
    <%= f.label :major %>
    <div>
      <%= f.select :major_eq, options_for_select(@options[:major], selected: handle_key_search("major_eq")),
        {include_blank: true} %>
    </div>
  </div>
  <div>
    <%= f.label :course %>
    <div>
      <%= f.select :course_eq, options_for_select(@options[:course], selected: handle_key_search("course_eq")),
        {include_blank: true} %>
    </div>
  </div>
   <%= f.submit %>
<% end %>

Trong ranssack có cung cấp form search_form_for, nó là phần mở rộng của form_for. Khi biểu mẫu được tạo bởi search_form_for được gửi, controller sẽ tạo Ransack::Search và xác thực nó.

Tiếp tục, trong ransack ta có thể thực hiện cả sort cho table. Bằng cách sử dụng trình trợ giúp chế độ xem sort_link, chúng ta có thể tạo các tiêu đề có thể nhấp vào các liên kết sẽ chuyển đổi giữa việc sắp xếp cột được chọn theo thứ tự tăng dần và giảm dần.

<table>
  <thead>
    <tr>
      <th>
        <%= link_to students_path(order_params("username", params)) do %>
          User name<%= raw order_icon("username") %>
        <% end %>
      </th>
      <th>
        <%= link_to students_path(order_params("email", params)) do %>
          Email<%= raw order_icon("email") %>
        <% end %>
      </th>
      <th>
        <%= link_to students_path(order_params("info_students.area", params)) do %>
          Area<%= raw order_icon("info_students.area") %>
        <% end %>
      </th>
      <th>
        <%= link_to students_path(order_params("info_students.major", params)) do %>
          Major<%= raw order_icon("info_students.major") %>
        <% end %>
      </th>
      <th>
        <%= link_to students_path(order_params("info_students.course", params)) do %>
          Course<%= raw order_icon("info_students.course") %>
        <% end %>
      </th>
      <th>
        <%= link_to students_path(order_params("info_students.phone", params)) do %>
          Phone<%= raw order_icon("info_students.phone") %>
        <% end %>
      </th>
    </tr>
  </thead>
  <tbody>
    <% @students.each do |student| %>
      <tr>
        <td><%= student.username %></td>
        <td><%= student.email %></td>
        <td><%= student.info_student.area %></td>
        <td><%= student.info_student.major %></td>
        <td><%= student.info_student.course %></td>
        <td><%= student.info_student.phone %></td>
      </tr>
    <% end %>
  </tbody>
</table>

Kết luận

Với Ransack bạn có thể xây dựng các form search từ đơn giản đến phức tạp bằng các truy vấn Ransack một cách dễ dàng. Bạn có thể tìm hiểu sâu hơn về ransack tại đây