accepts_nested_attributes_for メソッドを攻略する！

最初は何が何だか分からなかった"accepts_nested_attributes_for"なるメソッドですが、使っているうちに便利さを実感するようになりました。

[Active Recordはaccepts_nested_attributes_forメソッドの解説 （Railsガイド）](https://railsguides.jp/form_helpers.html#%E8%A4%87%E9%9B%91%E3%81%AA%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)
[Active Recordはaccepts_nested_attributes_forメソッドの解説 （Ruby on Rails英語リファレンス）](
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html)

要するに親モデルを保存するときに、Associationで関連づけた子モデルも一緒に保存することができるメソッドです。例えばuserが複数のブログを書いている場合。has_many と belongs_to をそれぞれ指定して、今回は user に accepts_nested_attributes_forメソッドを設定します。 

####modelの記述

```models/user.rb
class User < ApplicationRecord
  has_many :blogs, dependent: :destroy
  accepts_nested_attributes_for :blogs, allow_destroy: true
end
```
```models/blog.rb
class Blog < ApplicationRecord
  belongs_to :user
end
```
user.rb の方に記述している allow_destroy: true については[こちら](https://railsguides.jp/form_helpers.html#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B)に記述がありますが、関連付けられたオブジェクトをユーザーが削除できるようにするために設定します。よく分からない、という場合はとりあえず accepts_nested_attributes_forメソッドの後ろにとりあえず記述しておく、と覚えておくでもいいかもしれません。

####controllerの記述

```user_controller.rb
#説明に必要なアクションのみ抜粋しています
class UserController < ApplicationController

  def new
    @user = User.new
    @user.blogs.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to users_path
    else
      render :new
    end
  end

  private
  def user_params
    params.require(:user).permit(:name, :address, :age, blogs_attributes: [:title, :content, :blog_num])
  end

end
```
accepts_nested_attributes_forメソッドに関連するのは２か所。
>① new アクションの @user.blogs.new
>② user_params の blogs_attributes: [:title, :content, :blog_num] 

①は[Railsガイド](https://railsguides.jp/form_helpers.html#%E3%83%8D%E3%82%B9%E3%83%88%E3%81%97%E3%81%9F%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0)にも説明がありました。

>(Railsガイドより)
フィールドのセットが少なくとも1つはユーザーに表示されるように、コントローラで1つ以上の空白の子を作成しておくというのはよく行われるパターンです。

@user = User.new だけだと user の入力欄しか表示されないので、controller で①の @user.blogs.new を記述して、子要素の入力フォームを表示させる準備をしています（この後viewファイルでフォームを表示させる記述をする必要があります）。

そして分かりにくかったのが②です。
[Railsガイド](https://railsguides.jp/form_helpers.html#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9)によると
>（Railsガイドより）
accepts_nested_attributes_forヘルパーが受け取るのはこのようなパラメータの名前です。たとえば、2つの住所を持つユーザーを1人作成する場合、送信されるパラメータは以下のようになります。
{
　'person' => {
　　'name' => 'John Doe',
　　'addresses_attributes' => {
　　　'0' => {
　　　　'kind' => 'Home',
　　　　'street' => '221b Baker Street'
　　　},
　　　'1' => {
　　　　'kind' => 'Office',
　　　　'street' => '31 Spooner Street'
　　　}
　　}
　}
}
:addresses_attributesハッシュのキーはここでは重要ではありません。各アドレスのキーが重複していなければそれでよいのです。

つまり上のパラメーターの場合、 addresses_attributes のキーである kind と street が２回ずつ登場しますが、重複は気にせず１回ずつパラメーターに記述してあげればよいとのこと。

その結果strong parameterは下記のような形に。通常のstrong parameterの中に addresses_attributes を並べて、その中に[ ]でキーを並べています。

>(Railsガイドより)
def create
  @person = Person.new(person_params)
  # ...
end

>private
  def person_params
    params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
  end

今回の私のケースで言うと、親モデルの name, address, age に加えて子要素の title, content, blog_num というキーをルールに従って記述すると、下記のような形に。
####controllerの記述

```users_controller.rb
  def user_params
    params.require(:user).permit(:name, :address, :age, blogs_attributes: [:id, :title, :content, :blog_num])
  end
```
ちなみに blogs_attributes の後の :id というのがかなり重要だったことに後から気付いたのですが・・・ここは後日別の記事に書こうと思います。

そして新規作成欄。

```ruby:new.html.erb
<%= render "form" %>
```

```ruby:_form.html.erb
<%= form_with(model: @user, local: true) do |form| %>
    <%= form.label :名前 %>
    <%= form.text_field :name %>

    <%= form.label :住所 %>
    <%= form.text_field :address %>

    <%= form.label :年齢 %>
    <%= form.text_field :age %>

  <%= form.fields_for :blogs do | blog | %>

     <%= blog.label :title, "タイトル" %>
     <%= blog.text_field :title %>

     <%= blog.label :content, "本文" %>
     <%= blog.text_field :content %>

     <%= blog.label :blog_num, "記事番号" %>
     <%= blog.text_field :blog_num %>

  <% end %>
  <div><%= form.submit :登録する%></div>
<% end %>
```
新規作成画面でのポイントは ③fields_for になります。上のコードで言いますと親モデルの内容を form で受けて、その後に続けて fields_for を記述します。形としては上のコードにあるように
>  <%= form.fields_for :blogs do | blog | %>

となります。blogs の内容を blog という形で１つずつ取り出して、 label で入力欄の説明、 text_field で入力欄を表示させる形。この辺は accepts_nested_attributes_for を使わない場合でも同じですね。

####まとめ
accepts_nested_attributes_for メソッドは最初苦手だったのですが、Railsガイドが結構分かりやすく説明してくれていたので助かりました。今回の記事を書くにあたってもRailsガイドをかなり参照しています。

個人的にポイントは下記の３点でした（今回のモデルの場合）。
①controller に @user.blogs.new を設定する
②user_params の blogs_attributes の記述
③view の新規作成画面で fields_for の使い方

ちょっとややこしいですが、このあたりは「習うより慣れろ」で、動きを理解した上でたくさん使ってみて覚えてしまうのが正解の気がしています。