プログラミング学習 備忘録

Railsを学習していく上での技術メモ。学んだことや解決したエラーなどを記録していきます。

Ransackを使って検索機能を実装してみた

Ransackとは、railsで検索機能を実装する時にとても便利なgemである。

Gemなしで検索機能を実装しようとすると非常に手間がかかるので、ぜひ覚えておきたいgemの一つ(らしい)

Gemなしで検索機能を実装する方法については、こちらの記事がとてもわかりやすかったです。

【Rails】ransackとgemなしの検索機能を徹底解説!!これを読めば検索機能マスターに! | プロ教

今回は、掲示板一覧とブックマーク一覧に検索機能を追加する。

掲示板一覧に実装

Gemのインストール方法は通常通りなので割愛。 まずは掲示板一覧に実装してみる。 Boardsコントローラーのindexアクションを以下のように変更

#boards_controller.rb

#変更前
def index
    @Boards = Board.all.includes(%i[user bookmarks]).order(created_at: :desc).page(params[:page])
end

#変更後
def index
  @q = Board.ransack(params[:q])
  @boards = @q.result(distinct: true).includes(%i[user                            bookmarks]).order(created_at: :desc).page(params[:page])
end

コード解説

打ち込まれた検索ワードは、params[:q]により取得する事ができる。 そして、ransack(params[:q])によりBoardテーブルからその文字列を含むデータを引っ張ってくる。その結果を@q.resultにより取得している。 distinct: trueは、「子テーブルの条件により検索を行い、親テーブルの一覧を表示する場合」に必要になるので今回は不要だがつけておいて損はない。

次に、検索フォームを作る。

#boards/index.html.erb
<%= search_form_for(@q, url: boards_path, method: :get) do |f| %>
<div class="input-group mb-3">
    <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: "検索ワード" %>
    <%= f.submit (t 'defaults.search'), class: "btn btn-primary" %>
</div>
<% end %>

Ransackを導入したことにより、search_form_forヘルパーが使用可能に。 search_field :title_or_body_contで、titleかbodyのどちらかに入力された文字列が含まれる結果を検索する事ができる。 (title_contだとtitleだけ、body_contだとbodyだけから引っ張ってこれる。)

以上で実装完了。

ブックマーク一覧に実装

次にブックマーク一覧に実装してみる。 掲示板一覧と大体一緒なのだが、ブックマークしている掲示板のみから引っ張ってくる必要があるので少しコードが異なる。

#変更前
def bookmarks
    @boards =    current_user.bookmark_boards.includes(:user).page(params[:page])
end

#変更後
  def bookmarks
    @q = current_user.bookmark_boards.ransack(params[:q])
    @boards = @q.result(distinct: true).includes(:user).page(params[:page])
  end
#boards/bookmarks.html.erb
<%= search_form_for(@q, url: bookmarks_boards_path, method: :get) do |f| %>
<div class="input-group mb-3">
    <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: "検索ワード" %>
    <%= f.submit (t 'defaults.search'), class: "btn btn-primary" %>
</div>
<% end %>

以上で実装完了。 これでもいいのだが、コードがほぼ被ってるのでパーシャル化するとなお良い。

#boards/_search_form.html.erb
<%= search_form_for(q, url: url, method: :get) do |f| %>
<div class="input-group mb-3">
    <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: "検索ワード" %>
    <%= f.submit (t 'defaults.search'), class: "btn btn-primary" %>
</div>
<% end %>

このように記述し、urlとqは、renderで渡してやる事で、様々なビューに柔軟に差し込む事ができる。

#boards/index.html.erb
      <%= render 'search_form', q: @q, url: boards_path %>

#boards/bookmarks.html.erb
      <%= render 'search_form', q: @q, url: bookmarks_boards_path %>