0
0

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

Posted at

スクールの課題で、タスク管理アプリを作成中、ラベルの検索機能を実装した。初学者の私にとっては複雑な内容だったので、備忘録として残しておく。



まず、前提

バージョン

  • 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
postメソッドで、URL「/tasks」に飛んでいる。
スクリーンショット 2023-12-16 14.14.23.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
続いて、nameにしてみると、

<%= f.select :name  〜省略〜

スクリーンショット 2023-12-16 16.05.04.png
機能面には何も変化が無いので、やっぱり予想通り、名前の役割な感じ。でも、またまた試しにhogehogeにしてみた。すると、スクリーンショット 2023-12-16 16.08.27.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
これでわかるように、ラベル検索をした場合、上記のようなパラメータが送られてくる。つまり、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
なので、変数labelには、idが2のlabelsテーブルのデータが入る。
続いて、

label_task_id = label.connections.pluck(:task_id)

について。
まず、label.connectionsはアソシエーションになっているので、そのlabelに紐づいているtaskのデータを取り出すことができる。つまり今回はidが2のlabelに紐づいているtaskのデータを取り出すことができる。
スクリーンショット 2023-12-16 15.00.24.png
そして、特定のカラムの値だけ取得したい場合に使えるpluckメソッドを使って、label.connections.pluck(:task_id)とすることで、idが2のlabelに紐づいているtaskのデータの中の、task_idを配列で取得することができ、それを変数label_task_id の中に入れている。そして、

@tasks = Task.where(id: label_task_id)

で、そのidlabel_task_idtaskを探して@tasksに入れられ、無事に、検索したいlabelに紐づいているtaskだけが検索されるのでした!!

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0