ransackを使ったユーザー検索機能の実装
ransackについては以前ブログで何度かまとめました。日付検索や、プルダウン表示などをまとめていたと思います。
今回はユーザー検索のページと検索結果のページを分けて表示していこうと思います。
まず最初にGemfile
にransack
を追加して、bundle install
します。
gem 'ransack'
ユーザーコントローラのindex
アクションに検索できるような設定を行います。
def index @q = User.ransack(params[:q]) @users = @q.result(distinct: true) end
ユーザー検索機能をつけるusers/index.html.erb
ファイルに検索機能をつけました。
<% content_for(:title, 'User search') %> <p id="notice"><%= notice %></p> <h1>search</h1> # ここを追記 <%= search_form_for @q do |f| %> <%= f.search_field :name_cont, placeholder: 'user name' %> <%= f.submit class: 'btn btn-primary' %> <% end %> <table class="table table-sm col-12"> <thead class="thead-light"> <tr> <th class="text-center">nickname</th> <th class="text-center">name</th> <th colspan="5"></th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td class="text-center"><%= link_to user.nickname, user_diaries_path(user) %></td> <td class="text-center"><%= user.name %></td> <td class="text-center"><% if logged_in? && current_user != user %> <% if current_user.following?(user) %> <%= render 'relationships/unfollow_button', user: user %> <% else %> <%= render 'relationships/follow_button', user: user %> <% end %> <% end %></td> </tr> <% end %> </tbody> </table>
ユーザー一覧の上に検索フォームが作成されました。しかし私は検索フォームで入力した後に、その検索条件に一致するユーザー情報だけ表示させたかったので、少しだけ変更していこうと思います。
まず最初に検索するページと検索結果が表示されるページの二つのページが必要になるので、usersアクションにもう一つアクションを追加します。
class UsersController < ApplicationController def index @q = User.ransack(params[:q]) end def search @q = User.search(params[:q]) @users = @q.result(distinct: true) end end
ルーティングも追加します。
get 'search', to: 'users#search'
検索結果を表示するビューファイルを作成します。
Sayo-MacBook-Pro:emoji_diary SAYO$ touch app/views/users/search.html.erb
<% content_for(:title, 'search result') %> <table class="table table-sm col-12"> <thead class="thead-light"> <tr> <th class="text-center">nickname</th> <th class="text-center">name</th> <th colspan="5"></th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td class="text-center"><%= link_to user.nickname, user_diaries_path(user) %></td> <td class="text-center"><%= user.name %></td> <td class="text-center"><% if logged_in? && current_user != user %> <% if current_user.following?(user) %> <%= render 'relationships/unfollow_button', user: user %> <% else %> <%= render 'relationships/follow_button', user: user %> <% end %> <% end %></td> </tr> <% end %> </tbody> </table>
users/index.html.erbの記述も少し変更します。
<% content_for(:title, 'User search') %> <p id="notice"><%= notice %></p> <h1>search</h1> <%= search_form_for @q, url: search_path do |f| %> <%= f.search_field :name_cont, placeholder: 'user name' %> <%= f.submit class: 'btn btn-primary' %> <% end %>
url: search_path
を追記することでフォームを送信するurlを指定しています。
このままだと検索フォームに何も入力せずに送信するとユーザー情報を全件表示してしますので直していこうと思います。
調べてみるとフォームにrequired: trueをつけることで空白では送信できなくなるようだったので下記のようにしてみました。
<%= f.search_field :name_cont, placeholder: 'user name', required: true %>
あとは/search
に直接アクセスした時にユーザー一覧が表示されてしまったのでそれを回避しようと思います。
class UsersController < ApplicationController def search @q = User.search(search_params) @users = @q.result(distinct: true) end private def search_params params.require(:q).permit(:name_cont) end end
search_params
で送信する情報をname_cont
のみに限定しています。アクセスしてみるとparam is missing or the value is empty: q
というエラー出るようになったので成功です。