タスク管理アプリ　ラベルの検索機能の実装

スクールの課題で、タスク管理アプリを作成中、ラベルの検索機能を実装した。初学者の私にとっては複雑な内容だったので、備忘録として残しておく。<br>
<br>
まず、前提
## バージョン
- ruby 3.2.2
- Rails 6.1.7.6
-------------------------
tasksテーブルとlabelsテーブルは多対多の関係。間にconnectionsテーブルという中間テーブルを作成している。
今回のテーマは、ラベルの検索機能。（タスク名のあいまい検索機能とステータス検索機能の部分は既に実装済の状態）
完成した`tasks_controller.rb`は以下。
```tasks_controller.rb
class TasksController < ApplicationController

  def index
    if params[:task].present?
      if params[:task][:name] && params[:task][:status].present?
        @tasks = current_user.tasks.search(params[:task][:name], 
        params[:task][:status])
      elsif params[:task][:name]
        @tasks = current_user.tasks.name_search(params[:task][:name])
      elsif params[:task][:status]
        @tasks = current_user.tasks.status_search(params[:task][:status])
      elsif params[:task][:id]
        label = Label.find(params[:task][:id])
        label_task_id = label.connections.pluck(:task_id)
        @tasks = Task.where(id: label_task_id)
      end
    end

    if params[:sort_expired]
      @tasks = current_user.tasks.all.order(timelimit: "ASC")
    elsif params[:sort_rank]
      @tasks = current_user.tasks.all.order(rank: "ASC")
    elsif params[:task]
      #@tasks = @tasksということ（検索機能で絞られたタスクがそのまま、ということ）
    elsif 
      @tasks = current_user.tasks.all.order(created_at: "DESC")
    end

    @task = Task.new
  end

  省略
 
  private

  def task_params
    params.require(:task).permit(:name, :content, :timelimit, :status, :rank, { label_ids: [] })
  end
  
end
```

## form_forで見た目の検索部分を作る
まず、`index.html.erb`ファイルに、検索部分を作る。
スクールのテキストに公式カンファレンスに載っている下記コードを参考に実装してみましょうとある。
```
<%= form_for @post do |f| %>
  <%= f.select :person_id, Person.all.collect { |p| [ p.name, p.id ] }, include_blank: true %>
  <%= f.submit %>
<% end %>
```
色々とネットで検索してみたけど、これをどう置き換えれば良いのかさっぱりわからない。しかし、
```
Person.all.collect { |p| [ p.name, p.id ]
```
の部分は、今回ラベルの検索なのだから、`Person`部分は`Label`になるような気がした。
なので、
```
Label.all.collect { |label| [ label.name, label.id ]
```
としてみる。
続いて、
```
<%= form_for @post do |f| %>
```
の部分。@postを、`task`コントローラー内に作っている検索機能なので、@taskに変える。
`index`アクション内に@taskがないので、設定する。
```
@task = Task.new
```
これで実行してみる！
```views/tasks/index.html.erb
<%= form_for @task do |f| %>
  <%= f.select :id, Label.all.collect { |Label| [ Label.name, Label.id ] }, include_blank: true %>
  <%= f.submit %>
<% end %>
```
すると、なぜだか`new.html.erb`が出てしまった！なぜだ！`index.html.erb`が開かれてほしいのに。
ログを確認してみると、
![スクリーンショット 2023-12-16 14.05.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/b01baba7-a284-584c-5fda-9f39b5fa83ea.png)
postメソッドで、URL「/tasks」に飛んでいる。
![スクリーンショット 2023-12-16 14.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/802fe4e2-c4d6-1973-69bc-b97164bb7003.png)
postメソッドで、URL「/tasks」だと、`create`アクションに飛んでしまうので、`index`アクションになるようにしなければならない。(`create`アクションで`new`に飛ぶように設定していた)
そのため、`get`メソッドになるようオプションをつける。
```views/tasks/index.html.erb
<%= form_for @task, method: :get do |f| %>
  <%= f.select :id, Label.all.collect { |Label| [ Label.name, Label.id ] }, include_blank: true %>
  <%= f.submit %>
<% end %>
```
そうすると、お見事！`index`アクションへ飛ぶようになりました！

また、
```
<%= f.select :id  〜省略〜
```
の`：id` の部分。ここもどうするか迷ったのだけど、どうやら、名前をつけるためな雰囲気があるので何でも良い気がしたけど、実態を把握するために色々試してみた。
まず、`id`の場合だと、
![スクリーンショット 2023-12-16 16.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/c7f07d44-9411-4781-593d-5ee002be25b7.png)
続いて、`name`にしてみると、
```
<%= f.select :name  〜省略〜
```
![スクリーンショット 2023-12-16 16.05.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/50c1b2ce-e5a2-330b-b1de-d5b0b1e1653b.png)
機能面には何も変化が無いので、やっぱり予想通り、名前の役割な感じ。でも、またまた試しに`hogehoge`にしてみた。すると、![スクリーンショット 2023-12-16 16.08.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/66fe8f3d-f1ed-73e2-6e02-31207cb27c15.png)
エラーになってしまった！！　どうやら、`form_for`の後の＠taskと関連のあるものにしないとダメらしいです。これについては、まだ理解が甘い気がするので、以後続いて学習していきたい。


## コントローラー内に検索機能のロジックを作っていく
実際に実装したのは
```
elsif params[:task][:id]
  label = Label.find(params[:task][:id])
  label_task_id = label.connections.pluck(:task_id)
  @tasks = Task.where(id: label_task_id)
end
```
この部分です。
さて、どのようにロジックを作り上げていったかというと、
まず、
![スクリーンショット 2023-12-16 14.27.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/8791da9e-9e71-be07-7ad5-fbe497a4360a.png)
これでわかるように、ラベル検索をした場合、上記のようなパラメータが送られてくる。つまり、`params[:task][:id]`が存在する＝true になるので、
```
elsif params[:task][:id]
```
と条件付けをする。
そして、
```
label = Label.find(params[:task][:id])
```
`params[:task][:id]`には、検索したいラベルの`id`が入っている。（下記画像参照。`binding.pry`を実行して確認した。）
![スクリーンショット 2023-12-16 14.51.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/94562561-9e45-b6af-f0f1-78a178ffeea6.png)
なので、変数`label`には、`id`が２の`labels`テーブルのデータが入る。
続いて、
```
label_task_id = label.connections.pluck(:task_id)
```
について。
まず、`label.connections`はアソシエーションになっているので、その`label`に紐づいている`task`のデータを取り出すことができる。つまり今回は`id`が２の`label`に紐づいている`task`のデータを取り出すことができる。
![スクリーンショット 2023-12-16 15.00.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566175/3022496b-4e9e-7729-0b9e-2dc55a211802.png)
そして、特定のカラムの値だけ取得したい場合に使える`pluck`メソッドを使って、`label.connections.pluck(:task_id)`とすることで、`id`が２の`label`に紐づいている`task`のデータの中の、`task_id`を配列で取得することができ、それを変数`label_task_id` の中に入れている。そして、
```
@tasks = Task.where(id: label_task_id)
```
で、その`id`が`label_task_id`の`task`を探して@tasksに入れられ、無事に、検索したい`label`に紐づいているtaskだけが検索されるのでした！！

初学者の私にとってはとても新鮮な発見がいっぱいで、面白かったです！
では本日は以上！