LoginSignup
62
70

More than 3 years have passed since last update.

Railsでラベル機能(タグ付け)を実装する

Last updated at Posted at 2019-06-29

はじめに

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を関連付けする必要があります。
今回は多対多の関連付けを行うので、結合モデル・中間テーブル(交差テーブル)を作りましょう。

中間テーブルの作り方はこちらのドキュメントが詳しいです。
- Active Record の関連付け (アソシエーション) - Rails ガイド

rails generate model labelling task:references label:references

rails db:migrate

 
この時点でテーブル構造は以下のようになっていると思います。

db/schema/db
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を使って、多対多の関連付けを指定します。

app/models/task.rb
class Task < ApplicationRecord
  has_many :labellings, dependent: :destroy
  has_many :labels, through: :labellings
end
app/models/label.rb
class Label < ApplicationRecord
  has_many :labellings, dependent: :destroy
  has_many :tasks, through: :labellings
end

Labbelingモデルはこのようになっていると思います。

app/models/labelling.rb
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全体は例えばこのようになります。

app/views/tasks/_form.html.erb
<%= 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で受け取れるようにしましょう。
ラベルを配列で送っているので、書き方が少し複雑になります。

app/controllers/tasks_controller.rb
private

def task_params
  params.require(:task).permit(:title, { label_ids: [] })
end

これでタスク作成・更新時にラベルを関連付けすることができるようになりました。
※事前にラベルを作成しないと、ラベルがチェックボックスに表示されません。

タスクに関連付けられているラベルを表示できるようにする

タスク一覧画面に実装します。

app/views/tasks/index.html.erb
<% @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_controllerindexアクションに渡せるようにしましょう。

app/views/tasks/index.html.erb
<%= 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側はこのように書きます。

app/controllers/tasks_controller.rb
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が一致するラベルを持つタスクを取得できます。

完成イメージ

label.gif

参考

62
70
1

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
62
70