[Rails] ransack掲示板の検索機能を実装

学習記録

はじめに

掲示板の検索機能を実装する仕方を見ていきましょう。ransackというgemを使っていきます。

  • 掲示板一覧画面の検索フォームからは、すべての掲示板を検索対象にしましょう。
  • 掲示板ブックマーク一覧画面の検索フォームからは、ブックマークした掲示板を検索対象にしましょう。
  • タイトル(title)と本文(body)を検索対象にしましょう。

作業の流れ

  • ransackの導入
  • コントローラの編集
  • 検索フォームのパーシャルを作成する
  • パーシャルを呼び出す
  • search_form_forで指定するurlはrequest.pathではダメな理由

ransackの導入

ransackは、検索機能を実装するためのgemです。

GitHub - activerecord-hackery/ransack: Object-based searching.
Object-based searching. . Contribute to activerecord-hackery/ransack development by creating an account on GitHub.

Gemfileに記載しましょう。

Gemfile

gem 'ransack'

インストールします。

$ bundle install

コントローラの編集

掲示板一覧画面とブックマーク一覧画面に検索フォームを実装するので、indexアクションとbookamrksアクションを修正します。

def index
  @q = Board.ransack(params[:q])
  @boards = @q.result(distinct: true).includes(:user).order(created_at: :desc).page(params[:page])
end

def bookmarks
  @q = current_user.bookmark_boards.ransack(params[:q])
	@bookmark_boards = @q.result(distinct: true).includes(:user).order(created_at: :desc).page(params[:page])
end
用語説明
params[:q]ビューファイルから送られてくるパラメータです。
ransackメソッド送られてきたパラメータを元にテーブルからデータを検索するメソッドです。
resultメソッドransackメソッドで取得したデータをActiveRecord_Relationのオブジェクトに変換するメソッドです。
destinct: true結果の重複を防ぐオプションです。
page(params[:page])ページネーションを実装した際に使用したメソッドです。

今回のケースでは特にdistinct: true は必要ありません。

必要になるのは、掲示板と関連付けられたコメントなどを検索条件に絞り込んで、掲示板の検索結果を表示する場合です。distinct: true が無ければ、Aという掲示板に対して複数のコメントが検索に引っかかった場合、Aという掲示板が2回取得されて表示されてしまいます。

今回は掲示板だけのカラムを検索しているだけなので付けなくても大丈夫なのですが、検索時に重複したデータを取得したいケースは稀ですし、検索条件が後から変更された場合にエラーの原因になるかもしれないので、distinct: trueは癖として毎回つけることが推奨されています。

検索フォームのパーシャルを作成する

掲示板一覧画面とブックマーク一覧画面で検索フォームが共通するので、検索フォーム部分を部分テンプレート(パーシャル)にします。

<%= search_form_for q, url: url do |f| %>
  <div class="input-group mb-3">
    <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
    <div class="input-group-append">
      <%= f.submit  class: 'btn btn-primary' %>
    </div>
  </div>
<% end %>

コードを読み解く

search_form_for メソッド

ransackで用意されているメソッドです。form_forform_withのransack版というイメージで良いでしょう。

urlの指定により、どのアクションに向けてリクエストを投げるのか決まります。

urlを指定しない場合、ブックマーク一覧の検索フォームでリクエストするurlが、/boardsになってしまいます。これはBoardsController#indexにルーティングされます。

_cont

ransackで用意されている述語で、検索したワードが含まれているレコードを取得します。

title_or_body_cont というのは、titlebody のカラムのデータで部分的に一致しているレコードを取得するということです。

cont というのはcontainの省略です。

f.search_field メソッド

f.search_field とすると、検索フォームが作成されます。

検索フォームはinputタグに type="search" が記述されているフォームです。

この記述により、フォームの横に「x」ボタンが表示され、クリックすると入力した文字をリセットすることができます。

パーシャルを呼び出す

掲示板一覧画面とブックマーク一覧表示画面でそれぞれ検索フォームのパーシャルを呼び出します。

<%= render 'search_form', q: @q, url: boards_path %>
<%= render 'search_form', q: @q, url: bookmarks_boards_path  %>

search_form_forで指定するurlはrequest.pathではダメな理由

<%= search_form_for q, url: request.path do |f| %>

request.path とは、いまリクエストされているpathを返すメソッドです。

今回のケースではurlにrequest.path を設定するのでも問題はないのですが、後から他のビューファイルでも検索フォームを起きたいという場合に問題が出やすくなります。例えば、show.html.erbのようなファイルだと、urlパスがboards/1 のようになってしまいます。これでは再利用性の低いパーシャルファイルだと言えるでしょう。

できるだけrequest.path は使わず、呼び出し側で指定したパスをローカル変数(url)に渡すような設計にしましょう。

終わりに

検索機能は定番の実装なので、理解を深めておきましょう。

参考記事

ransackを使って検索機能がついたアプリを作ろう!
Railsの検索機能を簡単に作成できるgemのransackの使い方を解説しています。gemの導入方法から実際の使い方まで、この記事を読めばransackの使い方をマスターすることができます。アプリに検索機能をつけたい方は必見です。
WordPress › エラー
GitHub - activerecord-hackery/ransack: Object-based searching.
Object-based searching. . Contribute to activerecord-hackery/ransack development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました