🔖

【Rails】検索機能

2023/07/02に公開
2

本の投稿サイトに検索機能をgemを使わずに実装します。

実装機能の要件

実装機能の要件は以下の通り。

コントローラー アクション 用途
SearchesController search 検索を行う

ビュー: ヘッダー

  • ログインしている場合に限り、ヘッダーに検索窓・検索ボタンを設置。
  • 検索対象(ユーザーか投稿か)の選択かをプルダウンメニューで選択できること。
  • 完全一致, 前方一致, 後方一致, 部分一致の検索手法をプルダウンメニューで選択できること。

ビュー: 検索結果表示画面

  • 検索結果表示画面を作成し、検索結果を表示すること。

Controller作成

ターミナルで以下のコマンドを実行してコントローラを作成します。

ターミナル
rails g controller searches

Routingの設定

以下の通り検索機能にアクセスするためのルーティングを設定をします。

config/routes.rb
Rails.application.routes.draw do
 : #省略
 get "/search", to: "searches#search"
end

Viewの作成

  • 検索ボックスの作成:
app/views/searches/_form.html.erb
<% if user_signed_in? %>
  <%= form_with url: search_path, method: :get, local: true do |f| %>
    <%= f.text_field :content %>
    <%= f.select :model, options_for_select({"User" => "user", "Book" => "book"}) %>
    <%= f.select :method, options_for_select({"完全一致" => "perfect", "前方一致" => "forward", "後方一致" => "backward", "部分一致" => "partial"}) %>
    <%= f.submit "検索" %>
  <% end %>
<% end %>

解説:

  1. <% if user_signed_in? %>
    ユーザーがログインしているかどうかをチェックしています。ログインしている場合のみ検索フォームを表示します。
  2. <%= form_with url: search_path, method: :get, local: true do |f| %>
    フォームを作成するためのメソッドです。search_path に対して GET メソッドでフォームのデータを送信します。
  3. <%= f.text_field :content %>
    検索キーワードを入力するためのテキストフィールドを表示します。:content は検索キーワードのパラメータ名を指定しています。
  4. <%= f.select :model, options_for_select({"User" => "user", "Book" => "book"}) %>
    検索対象のモデルを選択するためのプルダウンメニューを表示します。:model は選択したモデルのパラメータ名を指定しています。options_for_select メソッドは、選択肢のハッシュを受け取り、プルダウンメニューを作ります。
  5. <%= f.select :method, options_for_select({"完全一致" => "perfect", "前方一致" => "forward", "後方一致" => "backward", "部分一致" => "partial"}) %>
    検索方法を選択するためのプルダウンメニューを表示します。
  6. <%= f.submit "検索" %> は、検索ボタンを表示します。ユーザーが入力した情報を送信するために使用します。
  • 部分テンプレートの呼び出し:
app/views/layouts/applictaion.html.erb
 <!DOCTYPE html>
 <html>
 :
   <body>
     <%= render 'layouts/header' %>
       <main>
         <%= flash[:notice] %>
+        <div class="d-flex justify-content-center mb-2">
+          <%= render "searches/form" %>
+        </div>
         <%= yield %>
       </main>
     <%= render 'layouts/footer' %>
   </body>
 </html>

解説:

  1. d-flex
    フレックスコンテナを作成するためのクラスです。
  2. justify-content-center
    水平方向に中央揃えを行うためのクラスです。
  3. mb-2: 下部に2単位のマージンを追加します。

完成:
以下のような検索ボックスができます。

Searchesコントローラへの記述

以下の通り記述します。

app/controllers/searches_controller.rb
class SearchesController < ApplicationController
  before_action :authenticate_user!
  
  def search
    @model = params[:model]
    @content = params[:content]
    @method = params[:method]
    
    # 選択したモデルに応じて検索を実行
    if @model  == "user"
      @records = User.search_for(@content, @method)
    else
      @records = Books.search_for(@content, @method)
    end
  end
end

解説:
以下にsearchアクションについて解説します。

  1. @model = params[:model]
    ユーザーが選択した検索対象のモデル("user" または "book")を @model 変数に代入しています。

  2. @content = params[:content]
    ユーザーが入力した検索キーワードを @content 変数に代入しています。

  3. @method = params[:method]
    ユーザーが選択した検索方法("perfect"、"forward"、"backward"、"partial")を @method 変数に代入しています。

  4. if @model == "user": 選択されたモデルが "user" である場合の条件分岐です。ユーザーの検索を行います。

  5. @records = User.search_for(@content, @method)
    選択された検索対象(ユーザー)に対して、search_for メソッドを呼び出して検索を実行し、結果を @records 変数に代入します。

  6. else: 上記の条件に当てはまらない場合の条件分岐です。"book"の検索を行います。

  7. @records = Books.search_for(@content, @method)
    選択された検索対象(書籍)に対して、search_for メソッドを呼び出して検索を実行し、結果を @records 変数に代入します。

Modelへの追記

Modelファイルに検索機能を追加していきます。

Userモデル:

app/models/user.rb
class User < ApplicationRecord
  :
  def self.search_for(content, method)
    if method == 'perfect'
      User.where(name: content)
    elsif method == 'forward'
      User.where('name LIKE ?', content + '%')
    elsif method == 'backward'
      User.where('name LIKE ?', '%' + content)
    else
      User.where('name LIKE ?', '%' + content + '%')
    end
  end
 :

Bookモデル:

app/models/book.rb
class Book < ApplicationRecord
  :
  def self.search_for(content, method)
    if method == 'perfect'
      Book.where(title: content)
    elsif method == 'forward'
      Book.where('name LIKE ?', content + '%')
    elsif method == 'backward'
      Book.where('name LIKE ?', '%' + content)
    else
      Book.where('name LIKE ?', '%' + content + '%')
    end
  end
  :

解説:

  1. このコードは、contentmethodという2つのパラメータを受け取ります。content は検索内容を表し、method は検索方法を表します。
  2. method の値に応じて以下の4つの条件分岐を行います。
  • perfect: 完全一致検索。content と完全に一致するタイトルを持つレコードを検索します。
  • forward: 前方一致検索。content で指定された文字列で始まるタイトルを持つレコードを検索します。
  • backward: 後方一致検索。content で指定された文字列で終わるタイトルを持つレコードを検索します。
  • partial: 部分一致検索。content で指定された文字列を含むタイトルを持つレコードを検索します。
whereメソッド・LIKE演算子について

検索結果表示Viewの作成

app/views/searches/search.html.erb
<% if @model == 'user' %>
    <h3>Users search for "<%= @content %>"</h3>
  <%= render 'users/index', users: @records %>
<% elsif @model == 'book' %>
    <h3>Books search for "<%= @content %>"</h3>
  <%= render 'books/index', books: @records %>
<% end %>

解説:
@model の値に応じて、検索結果の見出しと一覧表示を条件分岐しています。

  • @model'user' の場合、ユーザーの検索結果を表示します。
  • @model'book' の場合、書籍の検索結果を表示します。

完成:
"がんも"とUser検索すると、以下のような画面が表示されます。


以上、検索機能の実装でした。
gemの Ransack を使うと簡単に検索フォームを実装できるみたい。

続きはこちら。
https://zenn.dev/ganmo3/articles/21fc60ced769e3

Discussion

すぎすぎ

大変参考になりました!
貴重な共有をありがとうございます!

がんもがんも

すぎさん、こちらこそ嬉しいコメントありがとうございます😊
参考になったなら本当に嬉しい限りです😭
学びのアウトプット、必ず力になっていきますのでお互い頑張りましょうね♪