Formオブジェクトを用いて作成したデータを、特定のデータのみ削除する方法

#要点
+ 初心者がアプリを作るときの参考に
+ _dependent: :destroy_ を用いて外部キーの削除制限を外す
+ 下記のエラー(Mysql2エラー)を解消する方法

```
ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`-アプリ名-_development`.`-中間テーブル名-`, CONSTRAINT `fk_rails_~~~~~` FOREIGN KEY (`-商品のテーブル名-_id`) REFERENCES `-商品のテーブル名-` (`id`))):
```

#はじめに

メルカリのようなフリマアプリを作成中で、Formオブジェクトを用いて商品にタグ付けして出品できる機能を実装する所まで行いました
各モデルとコントローラーは以下のようになります

+ Item/商品
+ Tag/タグ
+ TagItemRelation/商品とタグの中間テーブル
+ TagsItem/ ItemとTagを同時に保存するためのFormオブジェクト

```ruby:/app/model/item.rb
class Item < ApplicationRecord
  has_many :tag_item_relations
  has_many :tags, through: :tag_item_relations
end
```

```ruby:/app/model/tag.rb
class Tag < ApplicationRecord
  has_many :tag_item_relations
  has_many :items, through: :tag_item_relations
  validates :tag_name, uniqueness: true
end
```


```ruby:/app/model/tag_item_relation.rb
class TagItemRelation < ApplicationRecord
  belongs_to :item
  belongs_to :tag
end
```

```ruby:/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  def save
    item = Item.create(item_name: item_name)
    tag = Tag.where(tag_name: tag_name).first_or_initialize
    tag.save
    TagItemRelation.create(item_id: item.id, tag_id: tag.id)
  end
end
```

コントローラー

```ruby:/app/controller/items_controller.rb
class ItemsController < ApplicationController
  def index
    @items = Item.all.order('created_at ASC')
  end

  def new
    @item = TagsItem.new
  end

  def create
    @item = TagsItem.new(items_params)
    if @item.valid?
      @item.save
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def items_params
    params.require(:tags_item).permit(
      :item_name,
      :tag_name
    )
  end
end
```

その後商品閲覧機能と
問題の__商品削除機能__を実装した

```ruby:/app/controller/items_controller.rb
class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :destroy]

###  中略

  def  show
    @tags_item = TagItemRelation.find(params[:id])
  end

  def destroy
    if current_user.id == @item.user_id
      @item.destroy
      redirect_to root_path
    else
      render :show
    end
  end

private

###  中略

  def set_item
    @item = Item.find(params[:id])
    @tag = Tag.find(params[:id])
  end
end

```

しかし実際に出品した商品を削除してみると、、、

```
ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`-アプリ名-_development`.`tag_item_relations`, CONSTRAINT `fk_rails_~~~~~~` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`))):
```

のエラーが出てしまい、商品の削除ができませんでした

#調べたこと
エラー内容をよく読んでみると、商品idは中間テーブルの__外部キー(foreign key)__に含まれて
デリートやアップデートができないよ！と書かれているのがわかります

```
ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`-アプリ名-_development`.`tag_item_relations`, CONSTRAINT `fk_rails_~~~~~~` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`))):
```

そこで外部キーについて調べてみると、、、
外部キー（外部制約キーとも）は、よくマイグレーションファイルなどに書いてるデータベースのキーで
データベースの特定の項目に好き勝手な値を入れることを防ぎ、外部の項目から選んで入れる制約を持ち
主に外部キーのテーブルから主キーのテーブルのデータに対して、__変更を制限__するものだそうです
今回の場合外部キーのテーブルは__tag_item_relations__、主キーのテーブルは__item__になることが分かりますね

# 外部キーの設定変更
今回のエラーの原因はマイグレーションファイルに記載した外部キー(foreign_key: trueのやつ)でした
ですがマイグレーションファイルごと書き換えてしまうと別の不具合が起きてしまう恐れがあります
今回はdestoryで外部キーとして設定している項目を削除させるように、__dependent: :destroy__を設定しようと思います
この簡単な設定により不具合なく商品の削除ができるようになります

```ruby:/app/model/item.rb
class Item < ApplicationRecord
  has_many :tag_item_relations, foreign_key: :item_id, dependent: :destroy
  has_many :tags, through: :tag_item_relations
end
```
#参考にしたサイト
[外部キー (foreign key)とは](https://wa3.i-3-i.info/word1992.html)

#関連記事
[Formオブジェクトを用いて作成したデータを、編集・更新する方法](https://qiita.com/ogamw/items/f81c36428ae358e084c2) ←次回記事、編集更新はこちら

[外部キー制約でハマったので(rails)](https://qiita.com/kemako/items/e98174b9747e0b2c75a6)

[外部キーを持つデータのdestroyを行うための設定](https://qiita.com/Ushinji/items/650fa295a3054d2fe582)