「Rails」の「accepts_nested_attributes_for」で苦戦したこと。

### ■環境
- macOS 10.13
- Rails 5.1
- Ruby 2.4
- mysql 5.7

## ■やりたかったこと
### **「accepts_nested_attributes_for」と、「allow_destroy: true」**
#### で、以下のような事例を、うまいこと解決したい！

### ■目標
商品を製造するときに、使用している原材料を複数の種類で登録したい。

- 部位によって、登録する原材料が異なるため、分類して登録しなければならない。
- 商品によっては、登録する分類と登録しない分類がある。

## サンプル

### ▼商品
- 商品1：みたらし団子
- 商品2：こしあん団子
- 商品3：ごまあん団子

### ▼原材料
- 材料1：白玉粉A
- 材料2：白玉粉B
- 材料3：砂糖
- 材料4：あずき
- 材料5：ごま
- 材料6：しょうゆ
- 材料7：しお

### ▼登録する分類
- お団子の主材料（分類：10）
- あん用の主材料（分類：20）
- あん用の副材料（分類：30）

<br>
***
## ■商品と原材料の組み合わせ例

### 「みたらし団子」のとき
- お団子の主材料
 - 白玉粉A
- あん用の主材料
 - しょうゆ
 - 砂糖
- あん用の副材料
 - しお

### 「こしあん団子」のとき
- お団子の主材料
 - 白玉粉B
- あん用の主材料
 - あずき
 - 砂糖
- あん用の副材料
 - しお

### 「ごまあん団子」のとき
- お団子の主材料
 - 白玉粉B
- あん用の主材料
 - ごま
- あん用の副材料
 - 砂糖
 - しょうゆ

<br>
***

## ■用意したテーブルの構造（ver.1.0）
##### [ products ] 商品マスタ
- id
- name（商品名）

##### [ product_classifications ] 原材料マスタ
- id
- name（原材料名）

##### [ main_product_classification_products ] お団子主材料の中間テーブル
- id
- product_id（商品テーブルのid）
- product_classification_id（原材料テーブルのid）

##### [ sub_product_classification_products ] あん用主材料の中間テーブル
- id
- product_id（商品テーブルのid）
- product_classification_id（原材料テーブルのid）

##### [ other_product_classification_products ] あん用副材料の中間テーブル
- id
- product_id（商品テーブルのid）
- product_classification_id（原材料テーブルのid）

###### ※便宜上、main、sub、otherと付けています。
<br>
#### ここまでは、ほぼ、レイルに乗せた標準の作りかなー、と思いながら....
<br>

#### ■ふと、コードを組んでいて思ったこと
##### 商品と原材料をつなぐ分類ごとの中間テーブルを作ったところまでは、よかったのですが、、、
###### 「登録する分類ごとに中間テーブルを用意するのは、どうなんだろう？」
###### 「登録する分類ごとに、ループで回してnewしたり、buildするのは、ソースが煩雑になりそうだなぁ。。。」
###### 「なんとか１個の中間テーブルに分類を設けて、綺麗にソースを書けないかなぁ？」
#### というのが、事の始まりでした。

##### まぁ、、、要は、楽できて綺麗にできたらいいな。と（笑）

## ■用意したテーブルの構造（ver.2.0）
##### [ products ] 商品マスタ
- id
- name（商品名）

##### [ product_classifications ] 原材料マスタ
- id
- name（原材料名）

##### [ product_classification_products ] 商品と原材料の中間テーブル
- id
- product_id（商品テーブルのid）
- product_classification_id（原材料テーブルのid）
- specify（分類明細）

## Modelの作りの見直し
```
原材料マスタmodel
class ProductClassification < ActiveRecord::Base
  belongs_to :product
end
```
```
中間テーブルmodel
class ProductClassificationProduct < ActiveRecord::Base
  belongs_to :product
  belongs_to :product_classification
end
```
```
main用の中間テーブルmodelをextendしたmodelを作る
class MainStuff < ProductClassificationProduct
end
```
```
sub用の中間テーブルmodelをextendしたmodelを作る
class SubStuff < ProductClassificationProduct
end
```
```
other用の中間テーブルmodelをextendしたmodelを作る
class OtherStuff < ProductClassificationProduct
end
```
### 拡張modelを使用して、商品マスタのhas_manyとaccepts_nested_attributes_forを設定する
```
商品マスタmodel
class Product < ActiveRecord::Base
  has_many :main_stuffs, -> { where(specify: 10) }
  has_many :main_product_classifications, through: :main_stuffs, source: :product_classification
  accepts_nested_attributes_for :main_stuffs, allow_destroy: true

  has_many :sub_stuffs, -> { where(specify: 20) }
  has_many :sub_product_classifications, through: :sub_stuffs, source: :product_classification
  accepts_nested_attributes_for :sub_stuffs, allow_destroy: true

  has_many :other_stuffs, -> { where(specify: 30) }
  has_many :other_stuff_product_classifications, through: :other_stuffs, source: :product_classification
  accepts_nested_attributes_for :other_stuffs, allow_destroy: true
end
```


## けつまづいたこと
##### 「specify」をどうやって、設定するの？
##### 「accepts_nested_attributes_for」まかせにしたから、newやbuildみたいに設定するソースコードがない！

###### ....うーん
###### ....うーん、うーん
###### ....うーん、うーん、うーん

##### 「ひょっとして...before_saveなら、accepts_nested_attributes_forでも、走るんじゃない？」
##### ということで、、、

<br>
##### 中間テーブルの各extendしたmodelを以下のようにしてみました。

```
class MainStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 10
  end
end
```
```
class SubStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 20
  end
end
```
```
class OtherStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 30
  end
end
```

## やった！　目標達成！
### 「accepts_nested_attributes_for」のすごさを改めて実感しました(~_~;;


## ■あとがき
##### 調べ方が悪かったと思いますが、今回のパターンのようなことを参考にできるサイトを見つけることができなかったので、自分への備忘録として書きましたが
##### もしかしたら、同じようなことで悩んでいる人のお役に立てるかも、という思いも強く、Qiitaに初投稿してみました。

##### 長々と読んでいただきありがとうございますm(_ _)m