【Rails】検索機能の実装※自己振り返り用



### ステップ 1: `searches` コントローラの作成

まず、検索専用のコントローラを作成し、ユーザーや投稿（Book）の検索ロジックを実装します。

```bash
rails g controller searches
```

- `rails g controller searches`: 検索機能用のコントローラを作成します。このコントローラで検索処理を行います。

#### コントローラの中身 (`app/controllers/searches_controller.rb`)

```ruby
class SearchesController < ApplicationController
  before_action :authenticate_user! # ログインしているユーザーのみが検索可能にする

  def search
    @model = params[:model]  # 検索対象（"user" か "book"）を取得するための変数
    @search_method = params[:search_method]  # 検索方法（完全一致、部分一致など）を取得するための変数
    @query = params[:query]  # ユーザーが入力した検索ワードを取得

    # 検索対象がユーザーの場合
    if @model == "user"
      @results = search_for_user(@search_method, @query) || []  # ユーザーを検索し、結果を@resultsに格納。結果がなければ空の配列を返す
    # 検索対象が投稿（Book）の場合
    elsif @model == "book"
      @results = search_for_book(@search_method, @query) || []  # 投稿（Book）を検索し、結果を@resultsに格納。結果がなければ空の配列を返す
    else
      @results = []  # モデルが指定されていない場合は空の配列を返す
    end
  end

  private
  
  # ユーザーを検索するメソッド。検索方法によって異なるクエリを実行
  def search_for_user(search_method, query)
    case search_method
    when "perfect"  # 完全一致検索
      User.where(name: query)  # ユーザー名が完全に一致するものを検索
    when "forward"  # 前方一致検索
      User.where("name LIKE ?", "#{query}%")  # ユーザー名が指定した文字列で始まるものを検索
    when "backward" # 後方一致検索
      User.where("name LIKE ?", "%#{query}")  # ユーザー名が指定した文字列で終わるものを検索
    when "partial"  # 部分一致検索
      User.where("name LIKE ?", "%#{query}%")  # ユーザー名のどこかに指定した文字列が含まれるものを検索
    end
  end
  
  # 投稿（Book）を検索するメソッド。検索方法によって異なるクエリを実行
  def search_for_book(search_method, query)
    case search_method
    when "perfect"  # 完全一致検索
      Book.where(title: query)  # タイトルが完全に一致する投稿を検索
    when "forward"  # 前方一致検索
      Book.where("title LIKE ?", "#{query}%")  # タイトルが指定した文字列で始まる投稿を検索
    when "backward" # 後方一致検索
      Book.where("title LIKE ?", "%#{query}")  # タイトルが指定した文字列で終わる投稿を検索
    when "partial"  # 部分一致検索
      Book.where("title LIKE ?", "%#{query}%")  # タイトルのどこかに指定した文字列が含まれる投稿を検索
    end
  end
end
```

---

### ステップ 2: ルーティング設定

検索機能用のルートを設定します。

#### `config/routes.rb`

```ruby
get 'search', to: 'searches#search', as: 'search'
```

- **`get 'search'`**: `GET`リクエストで`/search`というURLにアクセスした際、`searches#search`アクションを実行するルートを設定します。
- **`to: 'searches#search'`**: `searches`コントローラの`search`アクションを実行する指定です。
- **`as: 'search'`**: 名前付きルート`search_path`を作成し、ビューでこのルートを参照できます。

---

### ステップ 3: 検索フォームの作成

検索フォームを作成し、ユーザーが検索ワードを入力したり、検索対象や検索方法を選べるようにします。

#### 検索フォームのコード（`app/views/layouts/_header.html.erb`など）

```erb
<% if user_signed_in? %>  <!-- ユーザーがログインしている場合のみ表示 -->
  <%= form_with url: search_path, method: :get, local: true do %> <!-- 検索フォーム。送信先は`search_path`で、GETリクエストを送る -->
    <%= text_field_tag :query, params[:query], placeholder: "検索ワードを入力" %> <!-- 検索ワードの入力フィールド -->
    
    <%= select_tag :model, options_for_select([["User", "user"], ["Book", "book"]]), prompt: "検索対象を選択" %> <!-- 検索対象（ユーザー or 投稿）を選択するプルダウンメニュー -->
    
    <%= select_tag :search_method, options_for_select([["完全一致", "perfect"], ["前方一致", "forward"], ["後方一致", "backward"], ["部分一致", "partial"]]), prompt: "検索方法を選択" %> <!-- 検索方法（完全一致、部分一致など）を選ぶプルダウンメニュー -->
    
    <%= submit_tag "検索" %> <!-- 検索ボタン -->
  <% end %>
<% end %>
```

#### フォーム内の解説

- **`form_with`**: Railsでフォームを作成するためのヘルパーです。`url: search_path`でフォームの送信先を指定し、`GET`リクエストを使用しています。
  
- **`text_field_tag`**: ユーザーが入力する検索ワードのテキストフィールドです。`params[:query]`に入力内容が送られます。

- **`select_tag :model`**: 検索対象（ユーザー or 投稿）を選択するためのプルダウンメニューです。

- **`select_tag :search_method`**: 検索方法（完全一致、部分一致など）を選択するプルダウンメニューです。

- **`submit_tag`**: 検索を実行するためのボタンです。

---

### ステップ 4: 検索結果の表示

検索結果を表示するためのビューを作成します。ユーザー名や投稿タイトルをリスト形式で表示します。検索結果がない場合は、該当する結果がない旨を表示します。

#### 検索結果の表示（`app/views/searches/search.html.erb`）

```erb
<h2>検索結果</h2>

<% if @results.any? %> <!-- 結果が1件でもある場合 -->
  <% @results.each do |result| %> <!-- 検索結果を1つずつ表示 -->
  
    <% if @model == "user" %> <!-- 検索対象がユーザーの場合 -->
      <td>
        <%= image_tag result.get_profile_image, size: '100x100' %> <!-- プロフィール画像を表示 -->
      </td>
      <td>
        <%= link_to result.name, user_path(result) %> <!-- ユーザー名をリンクとして表示 -->
      </td>
    <% elsif @model == "book" %> <!-- 検索対象が投稿（Book）の場合 -->
      <td>
        <%= link_to result.title, book_path(result) %> <!-- 投稿タイトルをリンクとして表示 -->
      </td>
    <% end %>
    
  <% end %>
<% else %> <!-- 結果が1件もない場合 -->
  <p>該当する結果がありませんでした。</p> <!-- 該当する結果がないことを表示 -->
<% end %>
```

#### 検索結果表示の解説

- **`@results.any?`**: 検索結果が1件でもあるかを確認します。1件以上ある場合は結果を表示します。
  
- **`@results.each do |result|`**: 検索結果をループで1件ずつ処理し、表示します。

- **`@model == "user"`**: 検索対象がユーザーの場合、ユーザー名を表示します。`user_path(result)`でユーザー詳細ページへのリンクも設定しています。

- **`@model == "book"`**: 検索対象が投稿（Book）の場合、投稿タイトルを表示し、`book_path(result)`で投稿詳細ページへのリンクを設定しています。