【Rails】 あいまい検索機能の付け方

経緯
-
現在絶賛作成中のポートフォリオで、フリマアプリのラクマの仕上げに検索機能をの実装したいと思い作成。さほど難しくはなかったけれども、色々記載しておこうと思います。


環境
-
ruby 2.6.5
Rails 6.0.3.2
haml使用


イメージ図
-
<img width="1000" alt="ファイル名" src="https://i.gyazo.com/069f543074682935258ad726f7185bbc.png">

<img width="1000" alt="ファイル名" src="https://i.gyazo.com/53fe5ca474841c1f8364b985ebe1023f.png">

手順
-
1、ルーティングの設定
2、検索フォームを作成
3、モデルに定義
4、コントローラーに設定
5、ビューの作成

の５つの手順になります。
簡単なので、サックと出来ると思います。


ルーティングの設定
-
今回は、7つの基本アクション以外のアクションを定義します。
7つの基本アクションとは、`index` `new` `create`　等のアクションの事ですね。
先ずは、コードから

```routes.rb
resources :items do
  collection do
    get 'search'
  end
end
```
説明ですが、
今回は先程言った通り7つの基本アクション以外で定義します。要は新しいアクションを作るみたいなイメージです。
その為のコードが、`collection ~ end` の所です。
get の横の`search` が新しいアクション名です。

そして、`collection` ですが、ここはURLの指定先にidの必要の可否で変わります。
idの可とは、showアクションのような個々に登録されている商品の詳細ページなどに見にいく時にふられるものです（まぁ、ふられると言うと言葉のニュアンス的に違うのですが・・・）
なので、idなしで有れば<font color="red"><b>collection</b></font>
idありで有れば<font color="red"><b>member</b></font> となります。

今回は、特定のページにいく必要がないため、`collection`を使用してルーティングを設定してます。
<br>
<br>

検索フォームを作成
-
先ずは、コードから

```index.html.haml
= form_with(url: search_items_path, local: true, method: :get, class: "search-form") do |f|
  = f.text_field :keyword, placeholder: "検索する", class: "search-input"
  %label{for: "search-icon", class: "search-label"}
    %i.fas.fa-search
  .search-icon
    = f.submit "検索", class: "search-btn", id: "search-icon"
```

必要そうな所だけ説明します。

先ずは、urlの設定部分ですが、先程設定したsearchのルーティングで設定されるplefixを書きます。plefixをパスとして書く時は文字の最後に_pathをつけるような書き方をします。
調べ方は、ご存知だと思いますが、
ターミナルで `rails routes` です。

次に、まぁこれは無理に行う必要はないですが、上記の画像のように、ムシメガネ🔍みたいなアイコンとsubmit（送信ボタン）の紐付けについてです。
つまり、ムシメガネ🔍みたいなアイコンを押したら送信ボタンが押されるように設定していると言う事です。

やり方は、label：for と id で紐付けます。
上記のコードで言うと、
これと `%label{for: "search-icon", class: "search-label"}`
これで `= f.submit "検索", class: "search-btn", id: "search-icon"` です。
`search-icon` って言う同じ名前がありますよね。これで紐付けされています。

そして、今回は検索フォームに`f.text_field :keyword`と記述したので、このフォームに入力した値はコントローラーで`params[:keyword]`と書くことで取得する事ができます。
<br>
<br>

モデルに定義
-
ここも、コードから

```item.rb
def self.search(search)
  return Item.all unless search
  Item.where('name LIKE(?)', "%#{search}%")
end
```
言い忘れましたが、今回はitemって言う名前でモデル等を作ってます。

説明します。

検索したキーワードが含まれている投稿を取得するために、<b>whereメソッド</b>と<b>LIKE句</b>を利用します。
whereメソッドは、<font color="red">モデル名.where('検索対象となるカラムを含む条件式')</font>とする事でテーブル内の「条件に一致したレコードのインスタンス」を配列の形で取得できます。

LIKE句は、曖昧（あいまい）な文字列の検索をするときに使用するもので、whereメソッドと一緒に使います。
詳細は、省きますが今回は、searchが含まれるnameカラムのデータを検索するものです。
詳しく知りたい方は、下記のリンク先が分かりやすいと思います。
[【SQL】LIKE句の基本的な使い方～複数検索する場合の方法まで解説](https://style.potepan.com/articles/22072.html)

そもそも、searchって何？っと思われた方の向けに説明します。
要は引数の話です。
引数のsearch（1行目の`(search)`）は、検索フォームから送信されたパラメーターが入ります。イメージしやすく言うと、検索フォームに みかん と入力して送信ボタンを押すとこの引数に みかん が入ると言う事ですね。
後は、文法の話です。
この引数にみかんというデータが入れば定義の中のsearchもみかんになるって訳です。つまり、このまま、みかんネタで言えば、itemテーブルのnameカラムにみかんと言う文字が入っているものを検索するって言う事です。
なので、言ってしまえば、1行目の`(search)`はどんな単語でも良いって事ですね。

次に、`return Item.all unless search` ですが、これは読んで字のごとくですが、
search（さっきので言えばみかん）と言う文字がなかったら他全てを出力すると言う意味です。

そして、最後に１行目の`self.search`の説明です。
現時点だと、`.search`というメソッドは使うことができません（この後コントローラーで定義するのに使います）
が、モデルで設定することにより、コントローラーで使用することができるようになります。そして、モデルの中でメソッドを定義する際には、メソッド名の頭に`self.`を付けると事で、コントローラーで使えるクラスメソッドになります。

以上で、モデルに定義は終了です。
<br>
<br>

コントローラーに設定
-
はたまた、ここもコードから

```item_controller.rb
def search
  @items = Item.search(params[:keyword])
end
```
`@items = Item.search(params[:keyword])`のコードは、
上記で述べてきた、今迄の説明で事足りるかと思うので省略します。
<br>
<br>

ビューの作成
-
検索結果画面のビューの作成ですが、
ここは、`search.html.haml`と言うファイルを作成して、
eachメソッドを使って出力すればオッケーかと思います。



```search.html.haml
.contents__box
  - @items.each do |item|
  ここより下は今自分が作っているものに照らし合わせて作成お願いします。
         
```

これで、全ての実装が完了です！
実装出来なかったなどの不備が有ればご連絡ください！

ありがとうございました。