親子関係のあるモデルのテーブルの値を一度に保存する方法

## 自分用メモ

```ruby:Gemfile.rb
gem 'nested_form_fields'
```
gemをインストール。
このgemをインストールするとfields_forが使えるようになる。
fields_forは親以下の子に使うform_forのようなモノ。
fields_forを使うときは親よりもネストさせて書く。

### モデル関係
親：post
子：program
子：dealer
孫：program

```ruby:model/post.rb
  has_many :programs, inverse_of: :post, dependent: :destroy
  has_one :dealer

  accepts_nested_attributes_for :programs, allow_destroy: true
  accepts_nested_attributes_for :dealer, allow_destroy: true
```
postから見て子供であるprogramとdealerのアソシエーションを書く。

accepts_nested_attributes_forを書くことによって、モデル同士が関連付けられている時に、ネストさせることで一度にまとめて保存できる。
allow_destroy:trueをオプションに書くことによって、fields_forで保存した値を削除できるようになる。（この認識はちょっとあやふやです。）
※注意点
モデルはアソシエーションで単数なのか複数なのかきちんと意識すること。
最初dealerの記述をする時に、accepts_nested_attributes_for :dealers, allow_destroy: true　と書いてしまいうまくいかなかった。
モデルのdealerはhas_oneなので「s」はいらない。

```ruby:model/dealer.rb
  belongs_to :company
  belongs_to :post, optional: true
```

dealerに紐づく子供はいないのでこれだけ。
今回の話にcompanyは関係ないので割愛。

```ruby:model/program.rb
  belongs_to :post, optional: true 
  has_many :products, inverse_of: :program, dependent: :destroy

  accepts_nested_attributes_for :products, allow_destroy: true
```
programから見て親のpostと子供のproductとのアソシエーション。
postと同じようにaccepts_nested_attributes_forを記載。

```ruby:product.rb
  belongs_to :program, optional: true
  belongs_to :category
```
productに子供はいないのでこれだけ。
今回の話にcategoryは関係ないので割愛。

### コントローラー

```ruby:posts_controller.rb
def  new
    @post = Post.new
    dealers = @post.build_dealer
    programs = @post.programs.build
    products = programs.products.build
  end

  def  create
    @post = Post.new(post_params)
    @post.save
  end

private

    def post_params
      params.require(:post).permit(:facility_name, :address, :tell, :registration_date, :estimate_sheet, :delivery_date, :delivery_note, :postal_code, :kananame, :user_id, dealer_attributes:[:name, :kananame, :company_id, :post_id, :_destroy, :id], programs_attributes:[:software, :user, :post_id, :_destroy, :id,  products_attributes:[:thing, :category_id, :model_number, :program_id, :_destroy, :id]]).merge(user_id: current_user.id)
    end
```

大元の親であるposts_controllerのみにnew、createを定義するだけで大丈夫。
ポイントは以下。
・newアクションで子供、孫のbuildを作成する。
　今回で言えば、dealer、program、productのbuild作成。


・paramsのpermitではモデルの記述で**accepts_nested_attributes_for**の対象の子は、
　**dealer_attributes:**という風に　モデル名_attributes:と記述する。
・また、子供、孫のハッシュをかっこでネストさせる。
　今回のdealerに子供はいないのでとじかっこの位置には注意。
　programには子供であるproductがいるので、更にネストさせている。
・また、モデルでallow_destroy: trueを記載しているモデルにはpermitの中に**:_destroy と :id**を記述する。

・createアクションでは、モデルオブジェクトの作成はPost.new(post_params)と大元の親だけで大丈夫。
　変数@postに作成したモデルオブジェクトを入れ、@post.saveとすることで保存される。


###　View

```new.html.haml
= render 'partial/sidebar'
.main
  %span.main-child
    情報を登録してください
  .main-registration__date
    .main-registration__date-child
      登録日
    = form_for @post, url: posts_path do |f|
      =f.text_field :registration_date, placeholder: "登録日", type: "text", class:"registration__date-input-text"
      .main-facility
        .main-facility-child
          名前
        =f.text_field :facility_name, placeholder: "名前", type: "text", class:"facility-input-text"
      .main-kana
        .main-kana-child
          名前（カナ）
        =f.text_field :kananame, placeholder: "カナ", type: "text", class:"kana-input-text"

      .top
        %span.top-name
          ディーラーを登録してください
        .top-dealer
          .top-dealer-child
          = f.fields_for :dealer do |k|
            .top-dealer-child_box
              .top-dealer-child_box-company_text
                会社名
                .top-dealer-child_box-company
                  = k.collection_select :company_id, Company.all, :id, :name
              .top-dealer-child_box-name_text
                名前
                .top-dealer-child_box-name
                  = k.text_field :name, placeholder: "名前", type: "text", class:"dealer-input-text", id: "dealer_text"
              .top-dealer-child_box-kana_text
                名前（カナ）
                .top-dealer-child_box-kana
                  = k.text_field :kananame, placeholder: "カナ", type: "text", class:"dealer_kana-input-text", id: "dealer_kana_text"

      .best
        %span.best-name
          納品内容を登録してください
        .best-software
          .best-software-child
            ソフトウェア
          = f.fields_for :programs do |d|
            #best-software-child_box
              = d.text_field :software, placeholder: "name", type: "text", class:"program-input-text", id: "program_text"

            .products
              .products-text_box
                = d.fields_for :products do |s|
                  .products-text_box-category_text
                    カテゴリー
                    .products-text_box-category
                      = s.collection_select :category_id, Category.all, :id, :name
                  .products-text_box-product_text
                    ハードウェア
                    .products-text_box-product
                      = s.text_field :thing, placeholder: "products", type: "text", class:"products-input-text", id: "product_text"

            .footer
              = f.submit "送信", class:"products_button"
```

本当のコードは長すぎるので一部削除。
form_forを使って、まず親であるpostを設定。

**form_forの書き方**
= form_for **モデル**, url: **createのpath** do |f|
モデル部分にモデルオブジェクトをいれる、今回は@post。
url部分にはpostのcreateアクションのpathをいれる（コマンドrails routesでわかる）

続いてpostの子供であるdealerを設定。
子供以下は全てfields_forを使う。

**fields_forの書き方**
= **f**.fields_for :**モデル** do |k|
・モデル部分にシンボルを使ってモデル名を記載。
　ここでも単数なのか複数なのか意識すること。
・fields_forの前の**f**はpostのform_forで使っている変数の|f|←コイツ
　必ず親の変数をつけること。
・hamlの場合必ず親のform_forやfields_forよりも中にネストさせること。
※programのビューの塊がdealerのfields_forよりネストされていない理由はprogramがdealerと同じ位置の子供だから。
もしprogramがdealerの子供であればネストさせないといけない。

###fields_forさえ使いこなせれば、大抵の親子関係の値は一度に保存可能なはず。