学習記録

アウトプット用に作りました

ransackを使ったユーザー検索機能の実装

ransackについては以前ブログで何度かまとめました。日付検索や、プルダウン表示などをまとめていたと思います。

今回はユーザー検索のページと検索結果のページを分けて表示していこうと思います。

まず最初にGemfileransackを追加して、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というエラー出るようになったので成功です。