[Ruby on Rails]検索機能の実装　gem無し　初心者

<h2>検索機能の実装</h2>
検索対象は　ユーザーか本の投稿か選べる。
検索方法も、完全一致、前方一致、後方一致、部分一致と選べる。

![スクリーンショット 2021-07-08 12.51.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/9fc35c75-c4cc-9837-45ed-28353c1303d8.png)

![スクリーンショット 2021-07-08 12.52.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/d5071eba-45f0-c969-53bb-6f1ab64fb58c.png)


<h2>コントローラーの作成</h2>

```
rails g controller Searches search
```

<h2>ルーティング記述</h2>

```
get '/search', to: 'searches#search'
```

<h2>検索フォーム作成</h2>

ヘッダーに入れたいので、部分テンプレートにします。

```perl:application.html.erb
    <% if user_signed_in? %>
          <%= render 'layouts/search' %>
    <% end %>
```

```perl:views/layouts/_seach.html.erb
<div class="serch_form">
    <%= 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 %>
</div>

```

serch_pathルーティングに検索内容を渡しています。
ユーザーの入力した検索ワードは```content```としてコントローラーのアクションに送られます。
初めましてな記述はf.select~でしょうか。この記述でプルダウン（セレクトボックス）が作成できます。
```({"完全一致"=>"perfect","前方一致"=>"forward"...```この部分の記述ですが、
"選択肢で表示する文字"=>DBに渡す値（保存させる内容）となっています。

ちなみに...
```<%=f.select:model,options_for_select({"User"=>"user","Book"=>"book"}),include_blank:"選択"%>```　のように最後に```include_blank:"選択"```を追加するとこんな感じになります。今回の場合だと見にくいですね。

![スクリーンショット 2021-07-08 12.48.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/6e2090f1-e317-c2d7-0f77-6a336d7bcd59.png)



:fist:セレクトボックスの作成方法は以下のページで理解できました。

https://qiita.com/kawakami_shotaro/items/11a677bf34136cb7686d

まとめ：検索ワードはcontentで、検索対象はmodelで、検索方法はmethodでコントローラーに渡される。

<h2>コントローラー記述</h2>

```perl:seaches_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=Book.search_for(@content,@method)
	  end
	end
end
```
入力、選択された値をparamsで受け取って@~に代入しています。
paramsとは、フォームなどによって送られてきた情報（パラメーター）を取得するメソッドです。
その後に、```@model```の値が```user```だった場合と、```book```だった場合で条件分岐しています。
そして```@records```に入れているのは検索結果です。

<h2>モデルファイルに検索条件の分岐を書く。</h2>

ちなみにコントローラーの記述を少し変更すれば、
別にモデルファイルに書かなくても、コントローラー内に記載することも可能です。それについては後述します。

<details><summary>じゃあ！なんで！わざわざモデルファイルに書くのか！？</summary><div>
以下の記事でなんとなく...理解できます。
簡単にいうと、あんまりコントローラーに書くと理解しにくい。わかりにくいから。

https://qiita.com/leavescomic1/items/99f32f45cd04035f146c
</div></details>




```perl:book.rb
class Book < ApplicationRecord
	# 以下３行は検索機能と関係なしです。　
	belongs_to :user
	validates :title, presence: true
	validates :body, presence: true, length: {maximum: 200}
	
  def self.search_for(content, method)
    if method == 'perfect'
      Book.where(title: content)
    elsif method == 'forward'
      Book.where('title LIKE ?', content+'%')
    elsif method == 'backward'
      Book.where('title LIKE ?', '%'+content)
    else
      Book.where('title LIKE ?', '%'+content+'%')
    end
  end
end
```


```method == 'perfect'```は、完全一致です。（viewファイルでそう定義してます。）
```forward```は、前方一致。あとはわかりますよね。。



<h5>Like句で検索</h5>

[前方一致]
```モデル名.where('カラム名 like ?','検索したい文字列%')```
[後方一致]
```モデル名.where('カラム名 like ?','%検索したい文字列')```

以下の記事で詳しくわかります。

https://qiita.com/seri1234/items/765423c2c46ca4114da0

user.rbにも同様に記載してください。

```perl:user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :books
  attachment :profile_image, destroy: false

  validates :name, length: { minimum: 2, maximum: 20 }, uniqueness: true
  validates :introduction, length: { maximum: 50 }

  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
end
```


<h2>viewページ作成</h2>

<details><summary>部分テンプレート使わないとこんな感じ...長くて申し訳ないです。</summary><div>

```
<div class="container">
  <div class="col-xs-12">
    <% if @model == "user" %>
    <h2>Users search for '<%= @content %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Name</th>
                <th>Introduction</th>
            </tr>
        </thead>
    <% @records.each do |user| %>
        <tbody>
            <tr>
                <th>
                    <%= attachment_image_tag(user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:'40x40') %>
                </th>
                <th>
                    <%= user.name %>
                </th>
                <th>
                    <%= user.introduction %>
                </th>
            </tr>
        </tbody>
    <% end %>
    <% elsif @model == "book" %>
    <h2>Books search for '<%= @content %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Title</th>
                <th>Opinion</th>
            </tr>
        </thead>
        <% @records.each do |book| %>
        <tbody>
            <tr>
                <th>
                    <%= attachment_image_tag(book.user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:'40x40') %>
                </th>
                <th>
                    <%= book.title %>
                </th>
                <th>
                    <%= book.body %>
                </th>
            </tr>
        </tbody>
        <% end %>
    <% end %>
    </table>
  </div>
</div>
```

</div></detail>![スクリーンショット 2021-07-08 12.51.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/7f975d1b-7fb4-83a6-c657-e98ea9819b2a.png)

<details><summary>部分テンプレートを使う場合。</summary><div>

```perl: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 %>
```

```perl:users/_index.html.erb
<table class='table'>
  <thead>
    <tr>
      <th>image</th>
      <th>name</th>
      <th colspan="3"></th>
    </tr>
  </thead>
  <tbody>
    <% users.each do |user| %>
      <tr>
        <td><%= attachment_image_tag(user, :profile_image, :fill, 50, 50, fallback: "no-image-icon.jpg") %></td>
        <td><%= user.name %></td>
        <td><%= link_to 'Show', user_path(user), class: "user_#{user.id}" %></td>
      </tr>
    <% end %>
  </tbody>
</table>
```

```perl:books/_index.html.erb
<table class='table table-hover table-inverse'>
  <thead>
    <tr>
      <th></th>
      <th>Title</th>
      <th>Opinion</th>
      <th colspan="3"></th>
    </tr>
  </thead>
  <tbody>
    <% books.each do |book| %>
      <tr>
        <td><%= link_to user_path(book.user) do %>
          <%= attachment_image_tag(book.user, :profile_image, :fill, 50, 50, fallback: "no-image-icon.jpg") %>
          <% end %>
        </td>
        <td><%= link_to book.title, book_path(book), class: "book_#{book.id}" %></td>
        <td><%= book.body %></td>
      </tr>
    <% end %>
  </tbody>
</table>
```

</div></detaill>

<h2>検索条件の分岐をモデルファイルではなくコントローラーに書きたい場合</h2>

こちらの記事では、モデルファイルではなくコントローラーに記述しています。

https://qiita.com/IsakiMatsuo/items/69f406d85dbd0ba62625

<h2>その他、参考にさせていただいた記事</h2>

https://qiita.com/hapiblog2020/items/6c2cef49df5616da9ae3


<h2>以上です</h2>
記事を書きながら作成していったので、おそらく漏れはないかと思います...。