はじめに
掲示板の検索機能を実装する仕方を見ていきましょう。ransackというgemを使っていきます。
- 掲示板一覧画面の検索フォームからは、すべての掲示板を検索対象にしましょう。
- 掲示板ブックマーク一覧画面の検索フォームからは、ブックマークした掲示板を検索対象にしましょう。
- タイトル(title)と本文(body)を検索対象にしましょう。
作業の流れ
- ransackの導入
- コントローラの編集
- 検索フォームのパーシャルを作成する
- パーシャルを呼び出す
- search_form_forで指定するurlはrequest.pathではダメな理由
ransackの導入
ransackは、検索機能を実装するためのgemです。
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_for
やform_with
のransack版というイメージで良いでしょう。
urlの指定により、どのアクションに向けてリクエストを投げるのか決まります。
urlを指定しない場合、ブックマーク一覧の検索フォームでリクエストするurlが、/boards
になってしまいます。これはBoardsController#index
にルーティングされます。
_cont
ransackで用意されている述語で、検索したワードが含まれているレコードを取得します。
title_or_body_cont
というのは、title
かbody
のカラムのデータで部分的に一致しているレコードを取得するということです。
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)に渡すような設計にしましょう。
終わりに
検索機能は定番の実装なので、理解を深めておきましょう。
参考記事

コメント