Ruby on Railsでフォームの送信先メソッドの指定について(POST, PATCH)


前提条件

前提として、ユーザに関するコントローラ名:Users、モデル:User
ユーザ情報のデータベースを

id name email
integer string string

とします。

わからなかったこと

ユーザ登録とかログインとか色んな場面でフォームが出てきますね。
こういうやつ↓Railsチュートリアルの内容です。

ユーザを作成するアクションがcreateという名前だったとすると、登録の場合はここで入力したデータを使ってpostでcreateのアクションに送るわけです。
で、ひっかかったのが
・フォームに送るデータの名前はHTMLのどこで紐付けられているか
・送信ボタンを押したときのアクションがCreateアクションとどこで指定されているか

フォームに送るデータとCreateアクション内で使う変数の紐づけ

登録画面のHTMLを見てみましょう。

signup
<form class="new_user" id="new_user" action="/signup" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="dStrA86aGPc1kp8jqGNMcNMBE+Y1zBMR+4pshaErYfZeZUZ016a/lYG9TjTYMsy7SKhkdgyKtmoN1MpPskbcTg==" />

      <label for="user_name">Name</label>
      <input class="form_control" type="text" name="user[name]" id="user_name" />

      <label for="user_email">Email</label>
      <input class="form_control" type="email" name="user[email]" id="user_email" />

      <label for="user_password">Password</label>
      <input class="form_control" type="password" name="user[password]" id="user_password" />

      <label for="user_password_confirmation">Confirmation</label>
      <input class="form_control" type="password" name="user[password_confirmation]" id="user_password_confirmation" />

      <input type="submit" name="commit" value="Create my account" class="btn btn-primary" data-disable-with="Create my account" />
</form>

大事なのは各フォームの"name"属性で指定されている内容でした。
name="user[email]"だとすると、これが指定されているテキストボックスは
userモデルの"email"カラムに該当するデータの内容ということになります。

Railsにはコントローラ名やモデル名を使って勝手に名前をつけてくれる機能が満載なのですが、チュートリアルを読む際は、「Railsの仕様でつけられた名前なのか」それとも「任意で開発者がつけた名前なのか」が分かりづらいのが難点だと思います。その説明はしてほしかったです、、、。

送信ボタンを押したときのアクションの指定

上記のHTMLを描写するためのERbファイルを覗いてみます。
ボタンを押したらUsersコントローラのCreateアクションを実行したいのでした。

new.html.erb
<%= form_for(@user, url: signup_path) do |f| %>
 <%= render 'shared/error_messages' %>
  <%= f.label :name %>
  <%= f.text_field :name, class:'form_control' %>
  <%= f.label :email %>
  <%= f.email_field :email, class:'form_control' %>
  <%= f.label :password %>
  <%= f.password_field :password, class:'form_control' %>
  <%= f.label :password_confirmation, "Confirmation" %>
  <%= f.password_field :password_confirmation, class:'form_control' %>
  <%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>

書いてないと思います。指定するのは<% form_for(・・・・の部分というのはわかるのですが。

結論として、formのボタンを押したときのHTTPメソッドはPOSTとなっており、@userと書かれたときはusersコントローラの場合、何も指定しなくてもusersのcreateアクションを実行するという仕様のようです。

新規作成(POST)か更新(Patch)かをレコードの有無で見分けている

ただ、上記はユーザの新規作成のときのコードですが、Railsチュートリアルを読み進めていいくと、
ユーザの編集画面のコードが下記のように書かれています。

edit.html.erb
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>
      <%= f.label :name %>
      <%= f.text_field :name, class:'form-control' %>
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>
      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>
      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>
      <%= f.submit "Save changes", class: "btn btn-primary" %>
    <% end %>

特にメソッドの指定はしていないですね。同じですね。
これはデータベースに存在するレコードかどうか(.saveや.createなどで保存までされているレコード化)によってメソッドが決まるようです。
新規作成のページであっても更新のページであってもページを開く前(viewでHTMLを描写する前)に
コントローラでそのページと関連付けて定義されたアクションを実行します。

users_controller.rb
#各ページを描写する前に実行するアクション
  #新規作成のとき
  def new
    @user = User.new #"新規の"ユーザを作成している
  end

  #更新のとき
  def edit
    @user = User.find(params[:id])#"既存の"ユーザを参照している
  end

で、このあとにviewで描写されるフォームのアクションは@userが新規ユーザか既存のユーザかによって
postにするかpatchにするかを決めています。
新規ユーザか既存のユーザかを見分ける方法は.new_record?メソッドで見分けます。

> User.new.new_record?
=> true

> User.first.new_record?
  User Load (1.0ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> false