現在、プログラミングスクールにて、主にRubyやRuby on Railsを勉強している者です。この記事を通して少しでもお役に立てられますと幸いです。また、説明不足や誤りがございましたら申し訳ございません。
この記事は、前回の「Railsの form_with を用いた投稿と編集処理の流れ① pryを使用してインスタンスの中身も確認してみる。」の続きとなります。
今回は主に、form_withヘルパーを用いた編集処理の流れについてみていきたいと思います。
前提
- MVCの流れや一対多の関連付けについての多少の理解。
- Sorcery gemを使用しているため、current_userメソッドを使っています。
- 機能は最低限となっているため、パーシャル(フォーム用)等は使用しておりません。
- ルーティングは resources :posts を使用しており、標準的なRESTfulルートが生成されています。
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
投稿の編集の流れ
編集の流れは、(今回は)投稿一覧にある「編集」リンクのクリックから始まります。
<% @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をブラウザにレスポンスとして返します。
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インスタンスの中身を確認してみる。
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])
により、対象の投稿がデータベースから取得されていることも確認ができます。
編集フォーム
編集画面におけるedit.html.erbビューファイルには、新規投稿フォームのnew.html.erbと同様に、form_withヘルパーを用いたフォームが含まれています。
※画像は「登録」と表示されていますが、正しくは「更新」となります。
<%= 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アクションにデータを送信する流れになります。
updateアクション
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アクションに挟んで、中身を確認してみる。
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 を用いた編集処理の流れとなります。
特定の値がいつ、どのように生成されるかを把握しておくだけでもデバッグの効率が上がりそうなので、ここらへんの流れは意識しながら取り組んでいきたいと思います。
説明ができてない部分や言語化ができてない部分が多々あるかと思いますが、少しでも参考になりますと幸いです。
2回に分けてのご拝読、ありがとうございました。
資料