search
LoginSignup
49
Help us understand the problem. What are the problem?

posted at

updated at

Rails 検索機能の実装

##はじめに

閲覧ありがとうございます!
今回は「検索機能」の実装についてまとめていきます。

__ransack__という__Gemfile__を使うと
簡単に実装できるみたいですが、まだ学習しておりません...。

そのため、今回はransackを使わずに、
「検索機能」を実装していこうと思います!


ポイント
・複数のモデルを検索できるようにします。(今回はUserとBook)
・検索方法は完全一致、部分一致、前方一致、後方一致の4つ作ります。
・検索後は、検索結果一覧ページを表示します。

##目次
・開発環境
・前提条件
・ルーティング定義
・検索フォーム作成
・Searchesコントローラー作成
・モデル内に検索方法の分岐定義
・検索結果の一覧表示

##開発環境

・ruby: 2.6.3
・rails: 5.2.4.5
・OS: macOS Catalina ver10.15.7
・Cloud9

##前提条件

■下記モデル実装済み
・Userモデル
・Bookモデル

■device導入
■Bootstrap導入

##ルーティング定義
まずはルーティングから定義していきましょう。

routes.rb
get "search" => "searches#search"

検索ボタンが押された時、Searchesコントローラーのsearchアクションが実行されるように定義しました。

パス名は__「search_path」__です。


余談ですが、パス名は下記2種類の方法で確認することができます。
1:ターミナルにてrails routesを実行
2:サーバーを起動し、URL末尾に/rails/info/routesを追記

##検索フォーム作成

次に、検索フォームを設置します。
今回はヘッダー内に検索フォームを表示したかった為、部分テンプレートとして作成しています。

_serach.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 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としてアクションに送っています。


##Searchesコントローラーにアクション定義

まず、コントローラーを作成します。

ターミナル.
rails g controller searches

次に、searchアクションを定義します。

searches.controller.rb
class SearchesController < ApplicationController
  before_action :authenticate_user!

  def search
    @range = params[:range]

    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]


②if文を使い、検索モデルUserorBookで条件分岐させます。


③looksメソッドを使い、検索内容を取得し、変数に代入します。
検索方法params[:search]と、検索ワードparams[:word]を参照してデータを検索し、
1:インスタンス変数@usersにUserモデル内での検索結果を代入します。
2:インスタンス変数@booksにBookモデル内での検索結果を代入します。

これでsearchアクションは完成です。

##モデル内に検索方法の分岐定義

しかし、このままでは検索方法による切替が行われません。
そこで、各モデルに条件分岐を追記します。

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テーブル内のカラム名です。
適宜、適したカラム名を指定しましょう。

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つずつ取り出していきましょう。

search_result.html.erb
<h2>Results index</h2>

<table class="table table-hover table-inverse">
  <!--検索対象モデルがUserの時 -->
  <% if @range == "User" %>
    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.name %></td>
        </tr>
      <% end %>
    </tbody>
  <% else %>
    <!--検索対象モデルがUserではない時(= 検索対象モデルがBookの時) -->
    <tbody>
      <% @books.each do |book| %>
        <tr>
          <td><%= book.title %></td>
          <td><%= book.body %></td>
        </tr>
      <% end %>
    </tbody>
  <% end %>
</table>

これで完成です!お疲れ様でした!

##完成デモ

画面収録 2021-03-13 12.24.02.mov.gif

今回の実装はシンプルなものである為、
モデルが3つ以上の時など臨機応変に修正してください!

閲覧ありがとうございました!
誤字、脱字、知識の誤りなどあればコメントいただけると幸いです!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
49
Help us understand the problem. What are the problem?