【Rails】ransackを使用した検索機能

2023/11/10に公開

ransackというgemを使用してレビュー投稿のキーワード検索機能と、メニューのジャンル検索機能を作成したので、今回はキーワード機能を例に振り返りたいと思います。
結果的に検索機能はできましたが、これよりもっとわかりやすいやり方もあると思うのであくまで一例としてご紹介します。

ransackとは

rails用の検索機能を実装するためのgemになります。これを使用することで簡単に検索機能が作成されるとのことです。
以前、gemを使用しないパターンの検索機能の実装は経験があったのですが、それと比べてあっという間にできた感じでした。
https://zenn.dev/sudoukky/articles/43f4941489c707

手順

gemの導入

Gemfile
gem 'ransack'

コントローラへ記述

今回はレビュー投稿の一覧画面に検索窓を設置するためindexアクション内に記述。

reviews.controller.rb
def index
    @review = Review.new
    @q = Review.ransack(params[:q])
    if params[:q].present?
      @reviews = @q.result(distinct: true)
    else
      @reviews = Review.all
    end
  end

上記のif文にて、もし検索が実行されている場合は検索結果が、そうでない場合は全てのビューが表示されるようになります。

  • @q = Review.ransack(params[:q])
    Reviewモデルに対してransackメソッドを呼び出し、params[:q]というパラメーターを渡して、検索オブジェクトを@qというインスタンス変数に代入することを意味します。
ransackメソッド

ransackメソッドは、検索条件に合致するレコードを探すためのクエリを生成するメソッドです。

  • if params[:q].present?
    params[:q]が存在するかどうかを判定する条件分岐です。present?メソッドは、オブジェクトが空でないかどうかを返すメソッドです。
  • @reviews = @q.result(distinct: true)
    @qに格納された検索オブジェクトのresultメソッドを呼び出し、distinct: trueというオプションを渡して、重複を除いた検索結果を@reviewsというインスタンス変数に代入することを意味します。
resultメソッド

resultメソッドは、ransackメソッドで生成されたクエリを実行するメソッドです。

モデルへ記述

review.rb
 # ransack用の検索対象フィールドを指定する
  def self.searchable_attributes
    %w[title body]
  end
  searchable_attributes.each do |field|
    scope "search_by_#{field}", ->(keyword) { where("#{field} LIKE ?", "%#{keyword}%") }
  end
  
  def self.ransackable_attributes(auth_object = nil)
   ["title", "body"] # 検索可能な属性名を指定
  end
  
  def self.ransackable_associations(auth_object = nil)
    [] # 検索可能な関連名を指定
  end
  • %w[title body]
    titleとbodyという文字列を要素とする配列を作成するための記法です。searchable_attributesというクラスメソッドの中で定義されています。
  • searchable_attributes.each do |field|
    searchable_attributesメソッドで定義された配列の要素を順番に取り出すための繰り返し処理を行うための記法です。
  • scope "search_by_#{field}", ->(keyword) { where("#{field} LIKE ?", "%#{keyword}%") }
    RailsのActiveRecordにおけるスコープを定義するための記法です。
スコープとは

データベースから取得するレコードの条件を指定するためのものです。

ここでは、search_by_titleやsearch_by_bodyというスコープを定義しています。これらのスコープは、whereメソッドを使って、titleカラムやbodyカラムに対してLIKE検索を行うようになっています。

  • def self.ransackable_attributes(auth_object = nil)
    ransackが検索対象とする属性名を指定するためのクラスメソッドです。ここでは、titleとbodyという属性名を指定しています。
  • def self.ransackable_associations(auth_object = nil)
    ransackが検索対象とする関連名を指定するためのクラスメソッドです。ここでは、関連名を指定していません。

ビューへ記述

views/public/reviews/index.html.erb
<%= search_form_for @q, url: reviews_path, method: :get do |f| %>
  <%= f.search_field :title_or_body_cont, placeholder: 'キーワード検索' %>
  <%= f.submit '検索' %>
<% end %>
  • <%= search_form_for @q, url: reviews_path, method: :get do |f| %>
    search_form_forメソッドは、検索フォームを作成するためのヘルパーメソッドです。引数には、検索オブジェクト@qと、検索フォームが送信されたときのURLとHTTPメソッドを指定しています。
  • <%= f.search_field :title_or_body_cont, placeholder: 'キーワード検索' %>
    search_fieldメソッドは、検索フォームの入力フィールドを作成するためのヘルパーメソッドです。引数には、検索対象となる属性名と、プレースホルダーのテキストを指定しています。

完成

まとめ

前回のgemを使用しないで行ったやり方と違い、ルーティングやコントローラを新たに作成することもなく、アクションとモデルとビューの記載のみで作成ができました。検索窓の位置や表示する画面によって作成方法も異なり、初めて見たメソッドだらけで正直まだまだわかりません笑
いろいろなパターンを行い覚えていければと思います。

参考

https://www.sejuku.net/blog/100824
https://qiita.com/mmaumtjgj/items/8731a70b3f328770867c
https://zenn.dev/goldsaya/articles/0917c03bbb88cb

Discussion