【Ruby on Rails】初学者向け－開発の流れについて⑤　検索機能の追加

## Rails開発の流れについて

・Railsプロジェクトの作成とDockerの設定
[【Ruby on Rails】初学者向け－開発の流れについて①　プロジェクトの作成編](https://qiita.com/y-kida/items/2c8634ceb28a93c84bc9 "Qiita Home")

・Gem・DB設定とルーティング
[【Ruby on Rails】初学者向け－開発の流れについて②　Gem・DB設定とルーティング編](https://qiita.com/y-kida/items/1d34bf72a3296579f213 "Qiita Home")

・Bootstrapの導入
[【Ruby on Rails】初学者向け－開発の流れについて③　Bootstrapの導入編](https://qiita.com/y-kida/items/cb3723a344766fcaf15e "Qiita Home")

・一覧、新規作成、参照、更新、削除画面機能の追加vol.1
[【Ruby on Rails】初学者向け－開発の流れについて④　一覧、新規作成、参照、更新、削除画面機能の追加vol.1](https://qiita.com/y-kida/items/2d5edbe3be1cd7b7289c "Qiita Home")

・一覧、新規作成、参照、更新、削除画面機能の追加vol.2
[【Ruby on Rails】初学者向け－開発の流れについて④　一覧、新規作成、参照、更新、削除画面機能の追加vol.2](https://qiita.com/y-kida/items/ec0a89bf4c10d3c66e7a "Qiita Home")

**・検索機能の追加（今回学習するのはこちら）**
・DBやモデル周りの整備
・バリデーション機能追加
・ユーザー認証機能追加

（※ 他追加機能があれば随時記事にしていく予定です）

--------------------------------------------------------
**前提**
・Docker
・Ruby on Rails 7
・Mysql
・BootStrap
・VSCode

-------------------------------------------------------

**前回のおさらい**
前回はデータの編集・削除が行えるようにしました。
今回は一覧画面の検索機能について、まとめていきます。

<h1>検索機能の追加</h1>
現在一覧画面では表示機能のみの状態となっています。
ここに検索機能を追加していきたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/1458011e-c4b8-1f90-5a87-02fa50fd4369.png)


まず検索機能の種類について説明していきます。

**完全一致**
…文字数含め文字列全体が完全一致している

**部分一致**
…特定の文字列を含んでいる

**前方一致**
…前方の文字列が一致している

**後方一致**
…後方の文字列が一致している

-----------------------------------------------

この一覧画面においてはまず「タスク名」と「内容」の２つを検索条件にします。
ここで検索条件を入力するボックスを作成します。

```html:app\views\tasks\index.html.erb
<div class="d-flex align-items-center">
  <h1>タスク一覧</h1>
  <div class="ms-auto">
    <%= link_to '新規作成', tasks_new_path, class: 'btn btn-outline-dark'%>
  </div>
</div>
    <!--追記-->
    <div class="search_form">
        <%= form_with url: root_path, local: true, method: :get do |f| %>
        <%= f.label :title, 'タスク名' %>
        <%= f.text_field :title, value: params[:title] %>
        <%= f.label :explanation, 'タスク内容' %>
        <%= f.text_field :explanation, value: params[:explanation] %>
        <%= f.submit "検索", class: "btn btn-primary" %>
    <% end %>
  </div>
<h6>期限が過ぎていないタスク</h6>
<table class="table table-hover tasks__table">
  <thead class="table-primary">
    <tr>
      <th>ID</th>
      <th>タイトル</th>
      <th>内容</th>
      <th>作成日</th>
      <th>期限日</th>
    </tr>
  </thead>
  <tbody>
    <% @tasks.each do |task| %>
      <tr>
        <td><%= task.id %></td>
        <td><%= task.title %></td>
        <td><%= task.explanation %></td>
        <td><%= task.created_at %></td>
        <td><%= task.due_date %></td>
        <td><%= link_to '詳細', task_path(task), class: 'btn btn-outline-dark' %></td>
      </tr>
    <% end %>
  </tbody>
</table>
```

**<%= f.text_field :title, value: params[:title] %>**
…valueのシンボルの後のparams[:title]で、
　一度検索フォームに送った値を初期値として表示するように設定している。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/15fcdcc8-60cc-d114-9747-b1b3eb78c6ae.png)

次にtasks_controllerのindexアクションに下記を追記します。
```ruby:app\controllers\tasks_controller.rb
    def index
        # タスク名とタスク内容の両方が入力されている状態で検索ボタンが押下された場合
        if params[:title].present? && params[:explanation].present?
            @tasks = Task.where(title: params[:title]).where("explanation LIKE ?", "%#{params[:explanation]}%")
        # タスク名のみが入力されている状態で検索ボタンが押下された場合
        elsif params[:title].present?
            @tasks = Task.where(title: params[:title])
        # タスク内容のみが入力されている状態で検索ボタンが押下された場合
        elsif params[:explanation].present?
            @tasks = Task.where("explanation LIKE ?", "%#{params[:explanation]}%")
        # 何も入力されていない状態で検索ボタンが押下された場合
        else
            @tasks = Task.all
        end
    end
#~~~~~~~~~省略~~~~~~~~~~~~
```
if文の分岐についてはコメントにある通りです。

**present?**
…ある特定のオブジェクトが存在しているかどうかを確認するメソッド。
　存在する場合はtrue、存在しない場合はfalseを返す。

**モデル名.where(...)**
…モデルからデータを取得する際に条件を指定する方法の１つ。
　「.」をつけてwhere文を連結することで条件を追加することができる。
　**（部分一致の例）** Task.where("title LIKE ?", "%#{params[:title]}%")
 　　　→Taskモデルから、パラメータ(title)が部分一致(LIKE)しているデータをとってくる
　**（完全一致の例）** Task.where(title: params[:title])
 　　　→Taskモデルから、パラメータ(title)が完全一致しているデータをとってくる

ここまでで、検索条件を入力し、無事絞り込みを行えることを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/c4b45a5e-67dd-70e7-85a6-8f024be7b4fb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/2c2b6937-30f5-db17-f3bb-6550909024b0.png)

ついでなので作成日のフォーマットを変更 & 
かつ項目に値が入っていなくてもエラーが発生しないように修正 & 表を少し直します。

```html:app\views\tasks\index.html.erb
<div class="d-flex align-items-center">
  <h1>タスク一覧</h1>
  <div class="ms-auto">
    <%= link_to '新規作成', tasks_new_path, class: 'btn btn-outline-dark'%>
  </div>
</div>
    <div class="search_form">
        <%= form_with url: root_path, local: true, method: :get do |f| %>
        <%= f.label :title, 'タスク名' %>
        <%= f.text_field :title, value: params[:title] %>
        <%= f.label :explanation, 'タスク内容' %>
        <%= f.text_field :explanation, value: params[:explanation] %>
        <%= f.submit "検索", class: "btn btn-primary" %>
    <% end %>
  </div>
<h6>期限が過ぎていないタスク</h6>
<table class="table table-hover tasks__table">
  <thead class="table-primary">
    <tr>
      <th>ID</th>
      <th>タイトル</th>
      <th>内容</th>
      <th>作成日</th>
      <th>期限日</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <% @tasks.each do |task| %>
      <tr>
        <!--修正-->
        <td><%= task.id %></td>
        <td><%= task.try(:title) %></td>
        <td><%= task.try(:explanation) %></td>
        <td><%= task.try(:created_at).try(:strftime, '%Y年 %m月 %d日') %></td>
        <td><%= task.try(:due_date).try(:strftime, '%Y年 %m月 %d日') %></td>
        <td><%= link_to '詳細', task_path(task), class: 'btn btn-outline-dark' %></td>
      </tr>
    <% end %>
  </tbody>
</table>
```
**task.try(:title)**
…「try」を記述してこのように取得することで、
　もしtitleの中身がnilだったとしてもエラーが発生しないようになっています。

 **@task.try(:created_at).try(:strftime, '%Y年 %m月 %d日')**
…上記と同様nilだったとしてもエラーが発生しないようにしており、
　かつstrftimeで、日時を指定されたフォーマットに変換しています。

 次はDBやモデル周りの整備を行います。
 ざっくりとしていますが、詳細は次の記事でご紹介します。