【gemなし】Railsでラベル機能を作る

## 速攻でラベルのモッグを作りたい時のためにメモ

### rails new

```
$ rails _5.2.4_ new sample-relation -d postgresql --skip-bundle && cd sample-relation && bundle install && rails db:create
```

### scaffoldで無駄なファイルが生成されないよう設定

```/config/application.rb
module SampleRelation
  class Application < Rails::Application
    # ここから下を追加
    config.generators do |g|
      g.javascripts false
      g.helper false
      g.test_framework false
    end
  end
end
```

### postテーブル(投稿機能機能)をscaffoldで生成

```
$ rails g scaffold post title:string details:string
```

### labelテーブル(ラベル機能)をscaffoldで生成

```
$ rails g scaffold label title:string
```

### post_labelテーブル(postとlabelの中間)をmodelを生成

```
$ rails g model post_label post:references label:references
```

## アソシエーション
**親**

```post.rb
class Post < ApplicationRecord
  has_many :post_labels, dependent: :destroy, foreign_key: 'post_id'
  has_many :labels, through: :post_labels, source: :label
end
```

**中間**

```post_label.rb
class PostLabel < ApplicationRecord
  belongs_to :post, optional: true
  belongs_to :label, optional: true
end
```

**子（ラベル）**

```label.rb
class Label < ApplicationRecord
  has_many :post_labels, dependent: :destroy, foreign_key: 'label_id'
  has_many :posts, through: :post_labels, source: :post
end
```

## コントローラー

**パラメータにlabel_idsを追加**

```posts_controller.rb
  private
    def post_params
      params.require(:post).permit(:title, :details, label_ids: [] )
    end
```

## ビュー

**新規投稿画面**



```ruby:views/posts/new.html.erb

<%= form_with(model: post, local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.post :title %>
    <%= form.text_field :title %>
  </div>

  <div class="field">
    <%= form.post :details %>
    <%= form.text_field :details %>
  </div>

# 追加

  <div class="field">
  <% Label.all.each do |label| %>
      <% if action_name == 'new' || action_name == 'create' %>
        <%= form.check_box :label_ids, { multiple: true, checked: label[:checked], disabled: label[:disabled], include_hidden: false}, label[:id], "" %>
        <label><%= label.title %></label>
      <% elsif action_name == 'edit' || action_name == 'update' %>
        <%= form.check_box :label_ids, { multiple: true, checked: @task.label_ids.include?(label.id), disabled: label[:disabled], include_hidden: false}, label[:id], "" %>
        <label><%= label.title %></label>
      <% end %>
    <% end %>
  </div>

# ここまで

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>
```

**投稿一覧画面**

```ruby:views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Title</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
      <% post.post_labels.each do |post_label| %>
        <%= post_label.label.title %><br>
      <% end %>
        <td><%= post.title %></td>
        <td><%= link_to 'Show', post %></td>
        <td><%= link_to 'Edit', edit_post_path(post) %></td>
        <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New post', new_post_path %>
```

### めんどくさがりな方のためにラベルのシードデータの設定

```seeds.rb
  5.times do |i|
    Label.create!(title: "sample#{i + 1}")
  end
```

### migrate(テーブルをDBに反映)してseedデータを作ってrails s(サーバー起動)

```
$ rails db:migrate && rails db:seed && rails s
```

以上です！

## 追記

**編集時にラベルのチェックを全部外したのにupdateされなかったのでその修正**

```posts_controller.rb
  def update
    unless params[:task][:label_ids]
      @task.label_on_tasks.delete_all
    end
    respond_to do |format|
      if @task.update(task_params)
        format.html { redirect_to @task, notice: 'Task was successfully updated.' }
        format.json { render :show, status: :ok, location: @task }
      else
        format.html { render :edit }
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end
```