はじめに
RailsでTo Doアプリにラベル機能を実装しました。
今回はラベル機能の実装方法を紹介します。
前提として、私の環境は下記の通りです。
Ruby 2.6.3
Rails 5.2.3
目的
- タスク作成時・更新時に、事前に作成したラベルを複数付けられるようにする。
- タスクに関連付けられているラベルを表示できるようにする。
- タスクをラベルで絞り込みできるようにする。
実装方法
それでは実装していきましょう。
まずTaskモデル・コントローラー・ビューをscaffoldで一括生成します。
rails generate scaffold task title:string
次にLabelもscaffoldで一括生成してしまいましょう。
rails generate scaffold label name:string
タスクにラベルを付与するためには、TaskとLabelを関連付けする必要があります。
今回は多対多の関連付けを行うので、結合モデル・中間テーブル(交差テーブル)を作りましょう。
中間テーブルの作り方はこちらのドキュメントが詳しいです。
rails generate model labelling task:references label:references
rails db:migrate
この時点でテーブル構造は以下のようになっていると思います。
ActiveRecord::Schema.define(version: 2019_06_29_021027) do
create_table "labellings", force: :cascade do |t|
t.integer "task_id", null: false
t.integer "label_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["label_id"], name: "index_labellings_on_label_id"
t.index ["task_id"], name: "index_labellings_on_task_id"
end
create_table "labels", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tasks", force: :cascade do |t|
t.string "title"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "labellings", "labels"
add_foreign_key "labellings", "tasks"
end
TaskモデルとLabelモデルにhas_many :through
を使って、多対多の関連付けを指定します。
class Task < ApplicationRecord
has_many :labellings, dependent: :destroy
has_many :labels, through: :labellings
end
class Label < ApplicationRecord
has_many :labellings, dependent: :destroy
has_many :tasks, through: :labellings
end
Labbelingモデルはこのようになっていると思います。
class Labelling < ApplicationRecord
belongs_to :task
belongs_to :label
end
タスク作成・更新時にラベルを関連付けできるようにする
app/views/tasks/_form.html.erb
のform_withヘルパーの中に、ラベルを選択できるチェックボックスを作ります。
<%= form.collection_check_boxes(:label_ids, Label.all, :id, :name) %>
form全体は例えばこのようになります。
<%= form_with(model: task, local: true) do |form| %>
<% if task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% task.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div class="field">
<%= form.collection_check_boxes(:label_ids, Label.all, :id, :name) %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
次にチェックボックスで選択したラベルをtasks_controller.rb
のStrong Parametersで受け取れるようにしましょう。
ラベルを配列で送っているので、書き方が少し複雑になります。
private
def task_params
params.require(:task).permit(:title, { label_ids: [] })
end
これでタスク作成・更新時にラベルを関連付けすることができるようになりました。
※事前にラベルを作成しないと、ラベルがチェックボックスに表示されません。
タスクに関連付けられているラベルを表示できるようにする
タスク一覧画面に実装します。
<% @tasks.each do |task| %>
<tr>
<td><%= task.title %></td>
<td><%= link_to 'Show', task %></td>
<td><%= link_to 'Edit', edit_task_path(task) %></td>
<td><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<td>
<% task.labels.each do |label| %>
<%= label.name %>
<% end %>
</td>
</tr>
<% end %>
ラベルでタスクを絞り込みできるようにする
まずはタスク一覧画面にセレクトボックスを作成して、絞り込みたいラベルのidをtasks_controller
のindexアクション
に渡せるようにしましょう。
<%= form_with url: tasks_path, method: :get, local: true do |form| %>
<%= form.select("label_id", Label.pluck(:name, :id), { include_blank: true }) %>
<%= form.submit 'Search', name: nil %>
<% end %>
controller側はこのように書きます。
def index
@tasks = Task.all
@tasks = @tasks.joins(:labels).where(labels: { id: params[:label_id] }) if params[:label_id].present?
end
@tasks.joins(:labels)
でlabelsテーブルを結合します。
そして、where(labels: { id: params[:label_id] })
で、先ほど設定したセレクトボックスから送られてきたparams[:label_id]
と、idが一致するラベルを持つタスクを取得できます。