Rails 検索機能
検索機能の追加
ヘッダー下部に検索窓を設置する
gemfileを使うと簡単に実装できるようだが、今回は使わない方法で実装!
実装する機能
コントローラ
- searchesコントローラ追加
- searchアクション追加(用途:検索を行う)
ビュー
- ログインしている場合に限り、ヘッダーに検索窓・検索ボタンを設置すること
- 検索結果表示画面を作成し、検索結果を表示すること
- 検索対象(ユーザーか投稿か)の選択かをプルダウンメニューで選択できること
- 完全一致, 前方一致, 後方一致, 部分一致の検索手法をプルダウンメニューで選択できること
ルーティング定義
routes.rb
get "search" => "searches#search"
検索ボタンが押された時、Searchesコントローラーのsearchアクションが実行されるように定義。
検索フォーム作成
ヘッダーの真下に検索フォームを表示したかったため、部分テンプレートとして作成。
_search.html.erb
<% if user_signed_in? %>
<div class="search_form">
<%= form_with url: search_path, local: true, method: :get do |f| %>
<%= f.text_field :word %>
<%= f.select :range, options_for_select([['User'], ['Book']]) %>
<%= f.select :search, options_for_select([["完全一致","perfect_match"], ["前方一致","forward_match"], ["後方一致","backward_match"], ["部分一致","partial_match"]]) %>
<%= f.submit "検索", class: "btn-sm btn-primary" %>
<% end %>
</div>
<% end %>
~解説~
url: search_path
検索内容を、先ほど作成したルーティングに送信。
<%= f.text_field :word %>
検索内容を、wordとしてアクションに送る。
<%= f.select :range, options_for_select([['User'], ['Book']]) %>
複数のモデルを検索できるように実装する為、
UserモデルとBookモデルを選択できるようにしている。
選択したモデルをrangeとしてアクションに送っている。
<%= f.select :search, options_for_select([["完全一致","perfect_match"]以下略]) %>
検索手法を定義。
今回のようにカンマ区切りで複数定義することができる。
選択した検索手法をsearchとしてアクションに送っている。
application.html.erbに
<%= render 'partial/search' %>
コントローラー
rails g controller searches
searchアクションを定義。
searches.controller
class SearchesController < ApplicationController
before_action :authenticate_user!
def search
@range = params[:range]
@word = params[:word]
if @range == "User"
@users = User.looks(params[:search], params[:word])
else
@books = Book.looks(params[:search], params[:word])
end
end
end
〜解説〜
このコードで検索フォームからの情報を受け取っている。
検索モデル→params[:range]
検索方法→params[:search]
検索ワード→params[:word]
looksメソッドを使い、検索内容を取得し、変数に代入!
検索方法params[:search]と、検索ワードparams[:word]を参照してデータを検索し、
1:インスタンス変数@usersにUserモデル内での検索結果を代入。
2:インスタンス変数@booksにBookモデル内での検索結果を代入。
モデル内に検索方法の分岐定義
このままでは検索方法による切替が行われないので、
各モデルに条件分岐を追記。
user.rb
# 検索方法分岐
def self.looks(search, word)
if search == "perfect_match"
@user = User.where("name LIKE?", "#{word}")
elsif search == "forward_match"
@user = User.where("name LIKE?","#{word}%")
elsif search == "backward_match"
@user = User.where("name LIKE?","%#{word}")
elsif search == "partial_match"
@user = User.where("name LIKE?","%#{word}%")
else
@user = User.all
end
end
nameは検索対象であるusersテーブル内のカラム名
LIKEによるあいまい検索
SQLにはLIKE句を使ったあいまい検索の構文がある。
〜 WHERE 列名 LIKE '%検索値%'
〜 WHERE 列名 LIKE '検索値_'
% → 0文字以上の任意の文字列
_ → 任意の1文字
同様のことをRailsで実現するために、whereメソッドにSQLのLIKE句で検索条件を記述!
book.rb
# 検索方法分岐
def self.looks(search, word)
if search == "perfect_match"
@book = Book.where("title LIKE?","#{word}")
elsif search == "forward_match"
@book = Book.where("title LIKE?","#{word}%")
elsif search == "backward_match"
@book = Book.where("title LIKE?","%#{word}")
elsif search == "partial_match"
@book = Book.where("title LIKE?","%#{word}%")
else
@book = Book.all
end
end
titleは検索対象であるbooksテーブル内のカラム名
~解説~
検索フォーム作成時に記載した内容を見返してみる。
- 完全一致→perfect_match
- 前方一致→forward_match
- 後方一致→backword_match
- 部分一致→partial_match
送られてきたsearchによって条件分岐させて、
whereメソッドを使いデータベースから該当データを取得し、変数に代入する。
完全一致以外の検索方法は、
#{word}の前後(もしくは両方に)、_%_を追記することで定義することができる。
これにより、検索方法毎に適した検索が行われるようになった!
検索結果の一覧表示
searchesコントローラ内で、検索結果を代入したインスタンス変数(@usersと@books)に対し、each文をつかって1つずつ取り出していく。
<table class="table table-hover table-inverse">
<!--検索対象モデルがUserの時 -->
<% if @range == "User" %>
<h2>Users search for "<%= @word %>"</h2>
<thead>
<tr>
<th>image</th>
<th>name</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= image_tag user.get_profile_image(50, 50) %></td>
<td><%= user.name %></td>
</tr>
<% end %>
</tbody>
<% else %>
<!--検索対象モデルがUserではない時(= 検索対象モデルがBookの時) -->
<thead>
<tr>
<th></th>
<th>Title</th>
<th>opinion</th>
</tr>
<tbody>
<h2>Books search for "<%= @word %>"</h2>
<% @books.each do |book| %>
<tr>
<td>
<%= link_to user_path(book.user) do %>
<%= image_tag book.user.get_profile_image(50, 50) %>
<% end %>
</td>
<td><%= link_to book.title , book_path(book.id) %></td>
<td><%= book.body %></td>
</tr>
<% end %>
</tbody>
<% end %>
</table>
参照
https://qiita.com/hapiblog2020/items/6c2cef49df5616da9ae3
部分テンプレートなど使いもっとコード量を減らせる気がするが、
一旦完成!
Discussion