【Rails】fields_forでbuildするときは、親子関係が「has_many」か「has_one」で方法が違うので気をつけよう！

ミナミ([@minami_nakasato](https://twitter.com/MinamiNakasato))です。

# 🔶今回の目標🔶
親子関係にある

Aテーブル/モデル(親)
Bテーブル/モデル(子)

これら２つに対して、同じビュー内のフォームからデータを登録しよう！

# 🔶やり方🔶
> 「一つのテーブルだけを扱うであれば
"form_for" や "form_with"
で簡単にできるけど」

>「同じフォーム内で２つのテーブルのデータを変更するなんて」

> 「どうすればええんじゃろ〜」

そんなときに<font color="red">__「fields_for」__</font>が役に立ちます！

今回は親である「niwatori」モデルと、子である「hiyoko」モデルを例に取り組んでみましょう💪

## ステップ① モデルとマイグレーションファイルを編集

### 🐔親🐔
#### (a) モデルを作成

```ruby
% rails g model Niwatori
```

#### (b) モデルファイルを編集

```ruby
class Niwatori < ApplicationRecord
	belongs_to :user #注1
	has_one :hiyoko #注2
	accepts_nested_attributes_for :hiyoko #注3
end
```

【注1】
> Niwatoriのさらに親としてuserモデルがある設定です。養鶏場のオーナーだと思ってください。


【注2】
> 今回はあえて「has_one」を例に出して説明します。
「has_many」を例にした説明はネット上に沢山あるけど、「has_one」を例にした説明が少ないので。

【注3】
> ２つのモデルがアソシエーションで結びついている時、２つまとめてレコードを更新できるRailsメソッドです。
コントローラでの記述と組み合わせて使うので、これだけではまだ未完成。

#### (c) マイグレーションファイルを編集

先ほど「rails g model」コマンドを実行した結果、マイグレーションファイルも生成されたので記入しましょう。

```ruby
class CreateNiwatoris < ActiveRecord::Migration[6.0]
  def change
    create_table :niwatoris do |t|
      t.references :user, foreign_key: true
      t.string :niwatoriname
      t.timestamps
    end
  end
end
```

#### (d) テーブルを作成

```ruby
% rails db:migrate
```

### 🐥子🐥
#### (a) モデルを作成

```ruby
% rails g model Hiyoko
```

### (b) モデルファイルを編集

```ruby
class Hiyoko < ApplicationRecord
	belongs_to :niwatori
end
```

#### (c) マイグレーションファイルを書く

```ruby
class CreateHiyokos < ActiveRecord::Migration[6.0]
  def change
    create_table :hiyokos do |t|
      t.references :niwatori, foreign_key: true
      t.string :hiyokoname
      t.timestamps
    end
  end
end
```

#### (d) テーブルを作成

```ruby
% rails db:migrate
```

# ステップ② ビューを編集する

フォームを組み込みたいファイルに以下を追記。

```ruby
 = form_with model: @niwatori,local: true do |form|
   = form.text_field :niwatoriname
   = form.fields_for :hiyoko, @niwatori.build_hiyoko do |hiyoko|
     = hiyoko.text_field :hiyokoname
   = form.submit "投稿"
```

「fields_for」の部分を

```ruby
= f.fields_for :hiyoko do |hiyoko|
```

にしてしまうとフォームが表示されません！
必ず上記のように

```ruby
= f.fields_for :hiyoko, @niwatori.build_hiyoko do |hiyoko|
```

と書きましょう。

# ステップ③ コントローラを編集する

```ruby
class NiwatorisController < ApplicationController
  def new
    @niwatori = Niwatori.new
    @niwatori.build_hiyoko #注1
  end

  def create
    @niwatori = Niwatori.create(niwatori_params) #注2
  end

  private
    def niwatori_params
      params.require(:niwatori)
      .permit(:niwatoriname,
              hiyoko_attributes:[ #注3
              :id
              :hiyokoname
              ])
      .merge(user_id: current_user.id)
    end
end
```

【注1】__「build」ってなに?__
> 親モデルとアソシエーションを組んでいる子モデルのインスタンスを生成できるメソッドです。
外部キー(niwatori_id)にあらかじめ値が入ったインスタンスを作れます。

これで「fields_for」を利用したフォームがちゃんと表示されるようになりました。

ただし注意が必要！

親と子が「<font color="red">__１　対　多__</font>」、
つまり親が子を「<font color="red">__has_many__</font>」している場合は

「<font color="red">__親モデル.子モデル.build__</font>」

と書きますが、

親と子が「<font color="blue">__１　対　１__</font>」、
つまり親が子を「<font color="blue">__has_one__</font>」している場合は
「<font color="blue">__親モデル.build_子モデル__</font>」
と書きましょう！

ここを間違えると、「fields_for」のフォームがビューで表示されません！

【注2】
> ストロングパラメータで値を引っ張ってきましょう

【注3】
> 子モデルの値を受け取るために、
「fields_forの引数_attributes」
という書き方をします。

# 🔶まとめ🔶
これで複数のモデルを一つのフォームで変更できるようになりました🐔🐥

この記事があなたの役に立ちますように！

======================================
ミナミ([@minami_nakasato](https://twitter.com/MinamiNakasato))です。都内のベンチャー企業でwebデザイン／プログラミング／動画撮影や編集などをやっています。