はじめに
こんにちは。アメリカにて独学でエンジニアを目指している者です。
以前の記事にてform_with
のオプションはmodel:@user
という指定方法を説明しましたが、Railsが自動で判断してcreateかupdateのどちらかのアクションを割り当てるというものでした。
しかし、当然Userのviewでしか使用できないためsessionコントローラーでログインページを作成するとなった場合には使用できません。
そこで本日は、model:@user
を使う意義をあらめて確認したのち、今回のsessionコントローラーの場合はどのように定義をするのがよいかについて説明していきたいと思います。
model: @userを使う意義
もしmodel: @user
と書いた場合、Railsは以下のような振る舞いを行います。
-
@user
という変数に格納されたActiveRecordオブジェクト(例えば、Userモデルのインスタンス)とフォームを関連付ける。 - フォームのフィールドとモデルの属性が1対1で対応するため、フォームで入力された値がモデルの属性に自動的に紐づく。
- バリデーションエラーがある場合などには、フォームとモデルが連携し、エラーメッセージの表示やハイライトが可能になる。
model: @user
は、主に以下のケースで有用です。
- 新規ユーザー登録フォーム(
@user
を新規作成し、User.new
で生成したオブジェクトと関連付けてフォームを表示) - ユーザー情報編集フォーム(すでに保存されている
@user
を取得し、フォームを通じて更新)
このように、モデルの属性とフォームの入力欄を対応付け、フォーム送信後にモデルインスタンスを作成・更新するという流れが中心となります。
また、今回のようにSessionControllerでログイン機能を実装する場合は、そもそも@user
というインスタンス変数が定義されていませんし、@session
のようなものをActiveRecordで管理することも通常はありません。そのため、ログインフォームにmodel: @user
を使う意義がなく、エラーとなってしまいます。
ログインフォームの特徴
ユーザーがアプリケーションにログインするとき、基本的には以下のステップを経ます。
- ユーザーのメールアドレスとパスワードを入力する。
- 送信ボタンが押されると、サーバー側にパラメータがPOSTされる。
- サーバー側でメールアドレスとパスワードが正しいかチェックする。
- セッションを作成し(または更新し)、そのユーザーがログイン済みであることを保持する。
この一連の流れの中では、データベースに新規レコードを作成したり、モデルのレコードを更新するといった操作は基本的に行いません。あくまで既存のユーザーに対して"ログイン"という操作を行うだけだからです。
SessionControllerにおけるform_with
一方、SessionController
はセッションを扱うコントローラです。セッションは、そもそもデータベースのレコードではありません。ブラウザとサーバー間でやり取りされる一時的な情報であり、Cookiesなどを通じてログイン状態を保持します。
したがって、**セッションコントローラでmodel: @user
と書いてしまうと、そもそも存在しない@user
を参照しようとするため、意図した動作になりません。**また、以下の点で利点がありません。
- ユーザーモデルを新規作成・更新しない
- セッション用のオブジェクト(
@session
のようなもの)をActiveRecordで持つことは通常ない
そのため、セッションコントローラでログインフォームを扱う際は、url:
オプションにログイン処理のパスを指定し、scope:
でパラメータをどのようにネストするかを指定します。
例えば、
<%= form_with(url: login_path, scope: :session) do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "ログイン" %>
<% end %>
この場合、フォーム送信時のパラメータ構造は下記のようになります。
params = {
session: {
email: "ユーザーが入力したメールアドレス",
password: "ユーザーが入力したパスワード"
}
}
コントローラでは、params[:session][:email]
と params[:session][:password]
を使用してログイン認証処理を行う、という流れになります。
まとめ
- ログインフォームでは、モデルのレコードを作成・更新しないため、
model: @user
を使う必要がない。 - セッションコントローラでは、セッションの作成や削除などを行うだけで、ActiveRecordのモデルインスタンスとは直接連動していない。
- そのため、
form_with
のオプションとしてurl:
やscope:
を使って明示的にURLとネストキーを指定し、送信されたパラメータをログイン用に扱う。
これが、SessionControllerでform_with(model: @user)
が使えず、form_with(url: login_path, scope: :session)
のように書かれている理由です。ログイン処理の流れを把握し、モデルを不要とするケースではこうした書き方がRails的にも自然であると言えます.