Railsの form_with を用いた投稿の作成と編集の処理の流れ② pryでインスタンスの中身も確認。

:::note warn
現在、プログラミングスクールにて、主にRubyやRuby on Railsを勉強している者です。この記事を通して少しでもお役に立てられますと幸いです。また、説明不足や誤りがございましたら申し訳ございません。
:::

この記事は、前回の「Railsの form_with を用いた投稿と編集処理の流れ① pryを使用してインスタンスの中身も確認してみる。」の続きとなります。

https://qiita.com/zakkiiy/items/933dceb2982b4ac8684d

今回は主に、form_withヘルパーを用いた編集処理の流れについてみていきたいと思います。

## 前提
- MVCの流れや一対多の関連付けについての多少の理解。
- Sorcery gemを使用しているため、current_userメソッドを使っています。
- 機能は最低限となっているため、パーシャル（フォーム用）等は使用しておりません。
- ルーティングは resources :posts を使用しており、標準的なRESTfulルートが生成されています。


```ruby:app/config/routes.rb
Rails.application.routes.draw do
  resources :users, only: %i[new create]
  resources :posts
end
```

```
# 今回使用されるルーティング

posts_path	GET	/posts(.:format) posts#index
            POST	/posts(.:format) posts#create
edit_post_path	GET	/posts/:id/edit(.:format) posts#edit

```

## 実行環境
Ruby 3.2.2
Rails 7.0.8

# 投稿の編集の流れ
![スクリーンショット 2024-01-14 22.45.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3535583/40636461-e579-70a4-f67c-10cb83b84fe5.png)

編集の流れは、（今回は）投稿一覧にある「編集」リンクのクリックから始まります。

```erb:app/views/posts/index.html.erb
<% @posts.each do |post| %>
  <div class="p-4">
    <h3 class="text-lg font-semibold text-gray-800">タイトル：</h3>
    <p class="text-gray-600"><%= post.title %></p>
    <h3 class="text-lg font-semibold text-gray-800">説明：</h3>
    <p class="text-gray-600"><%= post.description %></p>
  </div>
  <span class="text-gray-700">投稿者: <%= post.user.first_name %></span>
  <%= link_to '編集', edit_post_path(post), class: "text-blue-600 hover:text-blue-800" %>
<% end %>
```

## editアクションの実行
今回のeditアクションは、既存の投稿を編集するために使用されます。ユーザーが編集リンク（edit_post_path(post)）をクリックすると、ブラウザはGETメソッドでPostsControllerのeditアクションをリクエストします。今回のeditアクションの主な目的は、編集したい投稿データを取得し、結果として関連するedit.html.erbをブラウザにレスポンスとして返します。

```ruby:app/controllers/posts_controller.rb
def edit
  @post = current_user.posts.find(params[:id])
end
```
上記のコードでは、params[:id]を使用して、編集する投稿のIDを取得します。
このIDは、先ほどユーザーがクリックした編集リンクのURLから渡される形となります。
今回の例でいえば
- `edit_post_path(post)`は`/posts/12/edit`のようなURLを生成する。（ここで12は投稿のID）
- `posts.find(params[:id])`を用いて、paramsから、受け取ったID（この例では12）に対応する投稿をデータベースから検索します

`@post = current_user.posts.find(params[:id])`により、ログイン中のユーザーに関連する投稿の中から、URLに含まれるID（params[:id]）に一致する投稿を検索し、@postに代入します。そして、後ほど出てくるform_withヘルパーを使うことで、@postのデータを基に編集フォームを生成し、ユーザーが投稿内容を編集できるようにします。

## binding.pryを使用して、postインスタンスの中身を確認してみる。
```ruby
    21: def edit
    22:   binding.pry
 => 23:   @post = current_user.posts.find(params[:id])
    24: end

[1] pry(#<PostsController>)> params
=> #<ActionController::Parameters {"controller"=>"posts", "action"=>"edit", "id"=>"12"} permitted: false>
[2] pry(#<PostsController>)> 
[3] pry(#<PostsController>)> @post = current_user.posts.find(params[:id])
  Post Load (0.3ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = $1 AND "posts"."id" = $2 LIMIT $3  [["user_id", 1], ["id", 12], ["LIMIT", 1]]
  ↳ (pry):2:in `edit'
=> #<Post:0x0000000**********
 id: 12,
 title: "テスト",
 description: "編集前のテスト",
 user_id: 1,
 created_at: Sun, 14 Jan 2024 14:47:07.180991000 UTC +00:00,
 updated_at: Sun, 14 Jan 2024 14:47:07.180991000 UTC +00:00>
[4] pry(#<PostsController>)> 
```
paramsメソッドによって、編集対象である投稿IDがリクエストから取得されていることが確認できます。また、`current_user.posts.find(params[:id])`により、対象の投稿がデータベースから取得されていることも確認ができます。

## 編集フォーム

![スクリーンショット 2024-01-15 13.16.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3535583/bb8a084d-c58f-68d8-5a8f-a91779e35a5d.png)

編集画面におけるedit.html.erbビューファイルには、新規投稿フォームのnew.html.erbと同様に、form_withヘルパーを用いたフォームが含まれています。
※画像は「登録」と表示されていますが、正しくは「更新」となります。

```erb:app/view/posts/edit.html.erb
<%= form_with model: @post do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %>
  <%= f.label :description %>
  <%= f.text_area :description %>
  <%= f.submit '更新' %>
<% end %>
```
この編集フォームの特徴は新規投稿時とは異なり、@postに既存の投稿データが格納されている点となります。form_withヘルパーは、渡されたモデルオブジェクト（この場合は@post）の現在の状態に基づいて動作します。@postがデータベースに既に存在するレコードである場合、form_withはその属性の値（例：タイトル、説明）をフォームのフィールドに自動的にセットします。この機能により、ユーザーはフォーム上で既存のデータを必要に応じて編集することができます。
今回であればユーザーが入力内容を修正して、「更新」ボタンをクリックすることで、PostControllerのupdateアクションにデータを送信する流れになります。

:::note info
なぜupdateアクションに送信されるのか？（前回記事と重複する内容です。）

form_withヘルパーは、データ送信先のURLを自動的に決定する機能を持っています。上記のように、form_with model: @post の場合、Railsはeditアクションで生成した@post の状態が新規レコードであるか既存のレコードであるかどうかを調べ、新規の場合は、createアクションで、既存の場合はupdateアクションに送信する流れとなっています。
:::


## updateアクション

```ruby:app/controllers/posts_controller.rb
def update
  @post = current_user.posts.find(params[:id])

  if @post.update(post_params)
    redirect_to posts_path
  else
    render :edit
  end
end
```
updateアクションは、編集フォームから送信されたデータを処理するために使用されます。このアクションでは、まずcurrent_user.posts.find(params[:id])を用いて、編集する投稿のデータを検索して特定します。ここで、params[:id]は編集対象の投稿IDを表し、この値を使用して、IDに紐づく投稿をデータベースから取得します。

続いて、@post.update(post_params)を実行することで、フォームから送信されたデータ（post_paramsによって安全にフィルタリングされたデータ）で対象の投稿を更新します。更新が成功すると、ユーザーは投稿一覧ページにリダイレクトされます。更新に失敗した場合は編集フォームが再度表示される状態となります。

## binding.pryをupdateアクションに挟んで、中身を確認してみる。

```ruby
25: def update
    26:   binding.pry
 => 27:   @post = current_user.posts.find(params[:id])
    28:   
    29:   if @post.update(post_params)
    30:     redirect_to posts_path
    31:   else
    32:     render :edit
    33:   end
    34: end

[1] pry(#<PostsController>)> params
=> #<ActionController::Parameters {"_method"=>"patch", "authenticity_token"=>
"7xZV2L*****", "post"=>{"title"=>"テスト", "description"=>"編集します。"}, "commit"=>"登録", 
"controller"=>"posts", "action"=>"update", "id"=>"12"} permitted: false>
[2] pry(#<PostsController>)> @post = current_user.posts.find(params[:id])
  Post Load (0.8ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = $1 AND "posts"."id" = $2 LIMIT $3  [["user_id", 1], ["id", 12], ["LIMIT", 1]]
  ↳ (pry):2:in `update'
=> #<Post:0x0000000******
 id: 12,
 title: "テスト",
 description: "編集前データ",
 user_id: 1,
 created_at: Sun, 14 Jan 2024 14:47:07.180991000 UTC +00:00,
 updated_at: Mon, 15 Jan 2024 05:14:56.825711000 UTC +00:00>
[3] pry(#<PostsController>)> if @post.update(post_params)
[3] pry(#<PostsController>)*   redirect_to posts_path
[3] pry(#<PostsController>)* else  
[3] pry(#<PostsController>)*   render :edit
[3] pry(#<PostsController>)* end  
  TRANSACTION (3.4ms)  BEGIN
  ↳ (pry):3:in `update'
  Post Update (0.8ms)  UPDATE "posts" SET "description" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["description", "編集します。"], ["updated_at", "2024-01-15 05:15:52.987589"], ["id", 12]]
  ↳ (pry):3:in `update'
  TRANSACTION (4.2ms)  COMMIT
  ↳ (pry):3:in `update'
Redirected to http://localhost:3000/posts
=> "<html><body>You are being <a href=\"http://localhost:3000/posts\">redirected</a>.</body></html>"
[4] pry(#<PostsController>)> @post
=> #<Post:0x0000000*******
 id: 12,
 title: "テスト",
 description: "編集します。",
 user_id: 1,
 created_at: Sun, 14 Jan 2024 14:47:07.180991000 UTC +00:00,
 updated_at: Mon, 15 Jan 2024 05:15:52.987589000 UTC +00:00>
[5] pry(#<PostsController>)> 
```

@post.update(post_params)を実行後、既存の投稿データが編集フォームに入力された値に変更され、データベースに正常に更新されたことが確認できます。このプロセスでは、@post インスタンスに紐づく投稿の内容（例えば、タイトルや説明文など）が、post_params メソッドを通じて取得した新しいパラメータで更新されています。

## 最後に

以上が、Railsの form_with を用いた編集処理の流れとなります。
特定の値がいつ、どのように生成されるかを把握しておくだけでもデバッグの効率が上がりそうなので、ここらへんの流れは意識しながら取り組んでいきたいと思います。

説明ができてない部分や言語化ができてない部分が多々あるかと思いますが、少しでも参考になりますと幸いです。
２回に分けてのご拝読、ありがとうございました。

## 資料

https://railsguides.jp/form_helpers.html