【Rails】form_withのHTTPメソッドの判断基準について【POSTかPATCHか】

## はじめに
　本記事は、プログラミングの学習を始めて1ヶ月の初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## 今回の疑問点
　今回の疑問点は、

　　_`form_with`はどのようにして`POST`と`PATCH`を判断しているのか_
　
　です。　

　非同期通信によるコメント機能を実装していた際に疑問を抱きました。
　納得のいく結論に至るまでかなり時間を要したのでまとめておきます。

## 疑問点についての解説

###結論
　モデルのインスタンスに対する`persisted?`の戻り値がtrueをなら`PATCH`、そうでなければ`POST`。つまり、データベースにインスタンスが保存されているかで判断している。

###`persisted?`とは
`persisted?`メソッドは、インスタンスがデータベースに保存済みかどうかをチェックするメソッドです。インスタンスがデータベースに保存されていればtrue、保存されていなければfalseを返します。

###検証

```book_comments_controller.rb

  def destroy
   @book = Book.find(params[:book_id])
   @book_comment = @book.book_comments.find(params[:id])
   p @book_comment.persisted?
   @book_comment.destroy
   p @book_comment.persisted?
   p @book_comment
   render 'books/show'
  end

```

 まず、コメントを削除するためのdestroyアクションを上記のように記述し、「削除」ボタンをクリックするとターミナルには以下のように出力されます。

```terminal

  ↳ app/controllers/book_comments_controller.rb:17
  BookComment Load (0.1ms)  SELECT  "book_comments".* FROM "book_comments" WHERE "book_comments"."book_id" = ? AND "book_comments"."id" = ? LIMIT ?  [["book_id", 1], ["id", 148], ["LIMIT", 1]]
  ↳ app/controllers/book_comments_controller.rb:18
true
   (0.1ms)  begin transaction
  ↳ app/controllers/book_comments_controller.rb:20
  BookComment Destroy (4.0ms)  DELETE FROM "book_comments" WHERE "book_comments"."id" = ?  [["id", 148]]
  ↳ app/controllers/book_comments_controller.rb:20
   (5.1ms)  commit transaction
  ↳ app/controllers/book_comments_controller.rb:20
false
#<BookComment id: 148, user_id: 9, book_id: 1, comment: "テスト", created_at: "2021-12-08 17:46:14", updated_at: "2021-12-08 17:46:14">
  Rendering book_comments/destroy.js.erb

(一部省略)

（renderされた画面で新規投稿）
Started POST "/books/1/book_comments"

```

注目していただきたいのが、
`true`
 `BookComment Destroy (4.0ms)  DELETE FROM "book_comments" WHERE "book_comments"."id" = ?  [["id", 148]]`
`false`
`#<Book id: 1, title: "gragrea", body: "gerageag", user_id: 1, created_at: "2021-12-02 07:52:27", updated_at: "2021-12-02 07:52:27">`
`#<BookComment id: 148, user_id: 9, book_id: 1, comment: "テスト", created_at: "2021-12-08 17:46:14", updated_at: "2021-12-08 17:46:14">`
`Started POST "/books/1/book_comments"`
です。
まず、
`true`
 `BookComment Destroy (4.0ms)  DELETE FROM "book_comments" WHERE "book_comments"."id" = ?  [["id", 148]]`　
`false`
の3文については、前後関係に注目してください。
`@book_comment.destroy`の前にある`p @book_comment.persisted?`の戻り値は`true`ですが、
`@book_comment.destroy`の後に記述した`p @book_comment.persisted?`の戻り値は`false`です。
このとこから、①destroyメソッドによってデータベースからレコードが削除されたことがわかります。
※②`@book_comment.destroy`をコメントアウトするとデータベースからレコードが削除されないため、コメントが削除されず、かつ、renderされた画面で新規投稿をするとHTTPメソッドはPATCHとなります。
次に、
`#<BookComment id: 148, user_id: 9, book_id: 1, comment: "テスト", created_at: "2021-12-08 17:46:14", updated_at: "2021-12-08 17:46:14">`
です。
上記の2文からは。③destroyメソッドが実行された後も`@book_comment`は値を持ち続けていることがわかります。
※値が残っているため、renderされた画面に投稿フォームとコメント一覧がありますが、投稿フォームに削除したコメントの内容が初期値として入力されます。
最後に
`Started POST "/books/1/book_comments"`
からは、④再度新規投稿を行うとHTTPメソッドは`POST`となっていることがわかります。

⑤`book_comments_controller.rb`ではモデルのインスタンスに対して実行しているのはnewメソッドではなく、findメソッドです。

###検証からわかること
①〜④から、form_withのHTTPメソッドの判断基準は、

form_withに渡すインスタンスが値を持っているか否かではなく（③、④）、また、モデルのインスタンスに対して実行しているメソッドがnewメソッドかfindメソッドかでもなく（⑤）、モデルのインスタンスがデータベースに保存されているか否かである（①、②）

ことがわかります。




## まとめ
判断基準について、モデルのインスタンスに対してnewメソッドが実行されていれば`POST`、findメソッドが実行されていれば`PATCH`と説明されていたり、form_withに渡すインスタンスが値を持っていれば、`PATCH`、空のインスタンスであれば、`POST`と説明されたりしていますが、上述の通り、

**データベースにインスタンスが保存されているか否かで判断**

していることがわかりました。