Ruby on Railsで、ネストした時のform_forの使い方

Ruby on Railsにて、config/routes.rbでネストした時の、ビューファイルにおけるform_forの使い方をメモ。

#前提
映画の一覧から好きな映画を選んでレビューを投稿・閲覧するアプリケーションを作成中。映画の情報を<strong>products</strong>テーブル、レビューの情報を<strong>review</strong>sテーブルに入れている。

#ルーティング
ルーティングはこの通り。

```ruby:routes.rb
TechReviewSite::Application.routes.draw do
  resources :products, only: :show do
    resources :reviews, only: [:new, :create]
    collection do
      get 'search'
    end
  end
  root 'products#index'
end
```
reviewsをproductsでネストしている。

例：reviews#new(reviewsコントローラのnewアクション)を呼び出すリクエスト
→/products/:product_id/reviews/new(.:format)

#コントローラでの処理
レビューを新しく書くために「投稿する」ボタンを押すと```/products/:product_id/reviews/new(.:format)```のGETリクエストが送られ、reviewsコントローラの```newアクション```が呼び出される。
リクエストが送られた際、パラメータのparams```[:product_id]```が一緒に送られる。

それを踏まえて、変数```@product```に```params[:product_id]```の数字をidにもつ```Productインスタンス```を代入。
また、Reviewクラスのインスタンスを新たに生成して```@review```に代入。

```ruby:reviews_controller.rb
class ReviewsController < RankingController
  def new
    @product = Product.find(params[:product_id])
    @review = Review.new
  end

  def create
    Review.create(create_params)
    redirect_to controller: :products, action: :index
  end

  private
  def create_params
    params.require(:review).permit(:nickname, :rate, :review).merge(product_id: params[:product_id])
  end
end
```

#form_forの使い方(ビューファイル)
さて、ビューファイルである```reviews/new.html.erb```。
```form_for```の使い方はこの通り。

```ruby:views/reviews/new.html.erb
            <%= form_for [@product, @review] do |f| %>
              <div style="margin: 8px 0">
                <%= f.label :nickname, style: { 'margin-right' => 8 } %>
                <%= f.text_field :nickname, placeholder: 'nickname', value: '' %>
              </div>
              <div style="margin: 8px 0">
                <%= f.label :評価, style: { 'margin-right' => 8 } %>
                <%= f.number_field :rate, placeholder: '評価', value: 1, max: 10, min: 1, html: { class: "search__query", style: 'text-align: right;' } %>
              </div>
              <div style="margin: 8px 0">
                <%= f.text_area :review, placeholder: 'レビューを書いてね！', style: 'width: 100%;height: 300px;' %>
              </div>
              <div class="row">
                <div class="col10 push1">
                  <%= button_tag type: "submit", class: "btn btn--block" do %>
                    投稿する<i class="icon-arrow-right"></i>
                  <% end %>
                </div>
              </div>
            <% end %>
```

ここで、```form_for```の引数を確認してみる。
引数は、配列```[@product, @review]```という引数になっている。
reviewsはproductsにネストされているので、引数となる配列は<strong>[親, 子]</strong>。

細かく見てみる。

```ruby
<%= f.text_field :nickname, placeholder: 'nickname', value: '' %>
```
の```:nickname```は、reviewの情報の一つ。(reviewsテーブルにはnicknameカラムがある)

以下も同様にreviewの情報。(rate)

```ruby
<%= f.number_field :rate, placeholder: '評価', value: 1, max: 10, min: 1, (以下略) %>
```

#submit後の動作
上記のフォームに入力された値を<strong>reviews</strong>テーブルに保存したい。
というわけで、投稿ボタンが押されたら(submit)、reviews#createへ繋げたい。

```
POST /products/:product_id/reviews(.:format)  
```
reviewsコントローラのcreateアクションでReviewクラスのインスタンスをcreateして、そこに保存。(その際privateで)
※createメソッドは、newとsaveを同時にできる。

保存後は作品一覧画面(products#index)にリダイレクトさせる。

```ruby:reviews_controller.rb
class ReviewsController < RankingController
  def new
    @product = Product.find(params[:product_id])
    @review = Review.new
  end

  def create
    Review.create(create_params)
    redirect_to controller: :products, action: :index
  end

  private
  def create_params
    params.require(:review).permit(:rate, :review).merge(product_id: params[:product_id], user_id: current_user.id)
  end
end
```