【Rails】カテゴリー機能の実装

# 目標

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579893/c3f6f0a2-9d4b-0e33-c1b3-cab2aa731a35.gif)

# 開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

# 前提

下記実装済み。

・[Slim導入](https://qiita.com/matsubishi5/items/b639ffddecc626856039)
・[Bootstrap3導入](https://qiita.com/matsubishi5/items/b117c95f4dd71340cc80)
・[投稿機能実装](https://qiita.com/matsubishi5/items/1a5aeab651b5822d1f2c)

# 実装

### 1.モデル

**①カテゴリーモデル・テーブルを作成**

```terminal:ターミナル
$ rails g model Category name:string
```

```terminal:ターミナル
$ rails db:migrate
```

```ruby:schema.rb
create_table "categories", force: :cascade do |t|
  t.string "name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end
```

**②booksテーブルにカラムを追加**

```terminal:ターミナル
$ rails g migration AddCategoryIdToBooks category_id:integer
```

```terminal:ターミナル
$ rails db:migrate
```

```ruby:schema.rb
  create_table "books", force: :cascade do |t|
    t.integer "user_id"
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "category_id"
  end
```

**③各モデルを編集**

```ruby:book.rb
# 追記
has_many :categories, dependent: :destroy
```

`dependent: :destroy`
➡︎ 該当のカテゴリーが削除された時、紐付いている本も一緒に削除する。

```ruby:category.rb
# 追記
belongs_to :book
```

### 2.コントローラー

**①`categories_controller.rb`を作成・編集**

予めindexとeditのビューを作成しておく。

```terminal:ターミナル
$ rails g controller categories index edit
```

```ruby:categories_controller.rb
class CategoriesController < ApplicationController
  before_action :set_category, only: [:edit, :update, :destroy]

  def index
    @category = Category.new
    @categories = Category.all
  end

  def create
    @category = Category.new(category_params)
    if @category.save
      redirect_to categories_path
    else
      @categories = Category.all
      render 'index'
    end
  end

  def edit
  end

  def update
    if @category.update(category_params)
      redirect_to categories_path
    else
      render 'edit'
    end
  end

  def destroy
    @category.destroy
    redirect_to categories_path
  end

  private

    def set_category
      @category = Category.find(params[:id])
    end

    def category_params
      params.require(:category).permit(:name)
    end
end
```

**②`books_controller.rb`のストロングパラメーターに`category_id`を追加**

```ruby:books_controller.rb
def book_params
  params.require(:book).permit(:title, :body, :category_id)
end
```

### 3.ルーティング

```ruby:routes.rb
# 追記
resources :categories, except: [:new, :show]
```

`except: [:new, :show]`
➡︎ newとshow以外のアクションをルーティングに追加する。

### 4.ビュー

```slim:categories/index.html.slim
.row

  .col-xs-3
    = form_with model: @category, local: true do |f|

      = f.label :name, 'カテゴリー名'
      br
      = f.text_field :name, class:'form-control'
      br

      = f.submit '追加', class: 'btn btn-primary btn-block'

  .col-xs-9
    table.table
      thead
        tr
          th
            | カテゴリー名
          th
          th

      tbody
        - @categories.each do |category|
          tr
            td
              = category.name
            td
              = link_to '編集', edit_category_path(category), class: 'btn-sm btn-primary'
            td
              = link_to '削除', category_path(category), method: :delete, data: { confirm: '本当に削除しますか？' }, class: 'btn-sm btn-danger'
```

```slim:categories/edit.html.slim
.row
  .col-xs-3

  .col-xs-6
    = form_with model: @category, local: true do |f|

      = f.label :name, 'カテゴリー名'
      br
      = f.text_field :name, class:'form-control'
      br

      = f.submit '保存', class: 'btn btn-primary btn-block'

  .col-xs-3
```

```slim:books/index.html.slim
.row
  .col-xs-3
    = form_with model: @book, local: true do |f|

      = f.label :title, 'タイトル'
      br
      = f.text_field :title, class:'form-control'
      br

      = f.label :body, '本文'
      br
      = f.text_area :body, class:'form-control'
      br

      / 追記
      = f.label :category_id, 'カテゴリー'
      br
      = f.collection_select :category_id, Category.all, :id, :name, { prompt: '選択してください' }, class: 'form-control'
      br

      = f.submit '投稿', class: 'btn btn-primary btn-block'

  .col-xs-9
    table.table
      thead
        tr
          th
            | 投稿者
          th
            | タイトル
          th
            | 本文
          / 追記
          th
            | カテゴリー
          th

      tbody
        - @books.each do |book|
          tr
            td
              = link_to book.user  do
                = book.user.name
            td
              = link_to book.title, book_path(book)
            td
              = book.body
            / 追記
            td
              = book.category.name
            td
              -if book.user == current_user
                = link_to '削除', book, method: :delete, data: { confirm: '本当に削除してもよろしいですか？' }, class: 'btn-sm btn-danger'
```

`= f.collection_select :category_id, Category.all, :id, :name`
➡︎ 全カテゴリーの`名前`をプルダウンメニューに表示し、値を`id`に設定している。