As-Is;To-Be
ユーザー登録機能に関し、現時点の状態をまとめると以下の通りになります。
- ユーザープロフィールページがひとまず形になった
- ユーザー登録フォームは未だ手つかず
すなわち、「/users/new ないしは/signup というページにスタブのみが存在しており、Webブラウザを使ったユーザー登録がまだできない」という状況です。
この状態から、「Railsチュートリアルで示されているユーザー登録フォームのモックアップのようなユーザー登録フォームを完成させ、Webブラウザを使ってユーザー登録できるようにする」というところまでがこの文章の範囲です。
forms_for
を使用する
Usersコントローラのnew
アクションに@user
変数を紐付ける
過去の学習の中で、config/routes.rb
には、既に「/signup を、Usersコントローラのnew
アクションに紐付ける」というルーティングを定義していました。次に必要となるのは、「Usersコントローラのnew
アクションを、User(モデル)オブジェクトのインスタンスである@user
に紐付ける」という手順です。
変更対象のコードはapp/controllers/users_controller.rb
です。以下の変更を行っていきます。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
+ @user = User.new
end
end
只今users#newに定義された動作は、「Userオブジェクトの新規インスタンスを生成し、それを@user
とする」という動作ですね。
ユーザー登録フォームのHTMLコードおよびSCSSを記述する
ユーザー登録フォームのHTMLコードは以下となります。「フォームHTML」の項で内容を詳しく見ていきます。
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
一方、CSSの追加は以下の通りです。
...略
+
+ /* forms */
+
+ input, textarea, select, .uneditable-input {
+ border: 1px solid #bbb;
+ width: 100%;
+ margin-bottom: 15px;
+ @include box_sizing;
+ }
+
+ input {
+ height: auto !important;
+ }
...略
この段階で、ユーザー登録フォームをWebブラウザで表示した結果は以下のとおりです。それらしいフォームが出来上がっていますね。

演習 - form_forを使用する
1. 試しに、リスト 7.15にある:name
を:nome
に置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?
app/views/users/new.html.erb
を、以下のように変更します。
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
- <%= f.label :name %>
+ <%= f.label :nome %>
- <%= f.text_field :name %>
+ <%= f.text_field :nome %>
...略
<% end %>
</div>
</div>
- HTTP側のレスポンスは「500 Internal Server Error」
- Rails側のエラーメッセージは
ActionView::Template::Error (undefined method
nome' for #<User:0x00007fbdc16d0408>`
以上のようになります。フォームの定義そのものを@user
から引いているので、Userオブジェクトに存在しない:nome
という属性が引けなくてエラーを返しています。
2. 試しに、ブロックの変数f
をすべてfoobar
に置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobar
とするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
app/views/users/new.html.erb
を、以下のように変更します。
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |foobar| %>
<%= foobar.label :name %>
<%= foobar.text_field :name %>
<%= foobar.label :email %>
<%= foobar.email_field :email %>
<%= foobar.label :password %>
<%= foobar.password_field :password %>
<%= foobar.label :password_confirmation, "Confirmation" %>
<%= foobar.password_field :password_confirmation %>
<%= foobar.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
こうした用途にfoobar
という名前が好ましくない理由としては、以下のようなものが考えられます。
- ブロック内でイテレータとして用いる一時変数の名前は、短いことが望ましい
- 一般に、変数の長さは生存期間と正の相関をもたせることが好まれる
-
foobar
という名前は、ブロックスコープの一時変数の名前として使うには長すぎる- そうした変数の名前には、
i
やj
などといった名前が使われるのが一般的 -
foobar
は、その長さからして、メソッドスコープの生存期間を持っていることが期待されるであろう
- そうした変数の名前には、
このあたりのルール設定については、名著として名高いリーダブルコードが参考になるかと思います。
フォームHTML
フォームの内容を定義するためのブロック
フォームHTMLの根幹となるのは、form_for
からend
までのブロックです。
<%= form_for(@user) do |f| %>
...略
<% end %>
重要なのは以下のポイントです。
-
@user
をフォームの処理対象のデータとする - 1つの変数をとるブロックである
- 変数(イテレータ)オブジェクトの名前は
f
である
- 変数(イテレータ)オブジェクトの名前は
-
f
オブジェクトには、HTMLフォームの様々な要素を返すメソッドが定義されている-
label
やtext_field
、password_field
など
-
ブロック内でフォームの諸アイテムを定義する
ラベルと入力領域の組
<%= f.label :name %>
<%= f.text_field :name %>
例えば上記のコードは、Userモデルのname
属性を設定するために用いる、ラベルと(単一行)テキスト入力領域の組を生成します。上記のコードに対応するHTMLは以下の通りです。
<label for="user_name">Name</label>
<input type="text" name="user[name]" id="user_name" data-kpxc-id="user_name">
:name
の他、:email
、:password
、password_confirmation
に対しても、同様にラベルとテキスト入力領域の組が定義されています。
また、Railsチュートリアル本文では、以下のポイントについても言及されています。
-
type="text"
とtype="email"
の違い- いずれも入力された内容をそのまま表示する単一行の入力領域である
-
type="text"
の場合、スマートフォンなどで表示すると通常のキーボードが表示される -
type="email"
の場合、スマートフォンなどで表示するとEメールアドレス入力に特化したキーボードが表示される
-
type="password"
の場合、入力した内容が隠蔽される単一業の入力領域となる
フォーム内諸要素のname
属性
<input type="text" name="user[name]" id="user_name" data-kpxc-id="user_name">
<input type="email" name="user[email]" id="user_email" data-kpxc-id="user_email" kpxc-username-field="true">
<input type="password" name="user[password]" id="user_password" data-kpxc-id="user_password" kpxc-password-generator="true" kpxc-pwgen-next-field-id="user_password_confirmation" kpxc-pwgen-next-is-password-field="false" kpxc-pwgen-next-field-exists="true" kpxcfields-onchange="true">
<input type="password" name="user[password_confirmation]" id="user_password_confirmation" data-kpxc-id="user_password_confirmation" kpxc-password-generator="true" kpxc-pwgen-next-field-exists="false" kpxcfields-onchange="true" kpxc-username-field="true">
上記はフォームHTMLのinput
要素のみを取り出したものです。モデルオブジェクトとの関係において、これらinput
要素の中で最も重要なのはname
属性です。name
属性の値は、当該フォームのsubmit時に生成されるクエリ文字列において、クエリパラメータの名前として用いられます(クエリ文字列およびクエリパラメータについては後で解説を入れます)。
form
要素そのもの
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post" kpxcform-initialized="true" kpxcusername="user_email" kpxcpassword="user_password">
</form>
上記はフォームHTMLのform
要素のみを取り出したものです。下記の埋め込みRubyに対応します。
<%= form_for(@user) do |f| %>
<% end %>
また、Usersコントローラのapp/views/users/new.html.erb
に対応する部分は下記になります。
def new
@user = User.new
end
モデルオブジェクトとの関係において重要なポイントは以下となります。
-
form
要素の生成に@user
が使われている- Railsにより、Userクラスのオブジェクトであることが認識される
- このフォームのメソッドは
post
であり、このフォームで生成されるHTTPリクエストの種類はPOST
である- Usersコントローラにより、
app/views/users/new.html.erb
に対応する@user
はUser.new
であることが示されている - Railsは、
new
で生成された@user
に対し、post
メソッドでフォームを構築すべきと自動で判断する - RESTの原則において、「新しいオブジェクトを生成する」という処理に対応するHTTPリクエストは
POST
であるため、RESTfulなアーキテクチャとして正しい挙動をする
- Usersコントローラにより、
-
form
要素のaction
属性は/users
である- このフォームでsubmitされたときの処理は、「
/users
にPOST
リクエストを送出する」というものになる
- このフォームでsubmitされたときの処理は、「
Railsで内部的に用いられるために自動で追加される要素
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="wwiu2NvcdY59JxI/Eq96qLM88Oe8oAsBPMM7RQ1JT4xO47Pt+wSscw8fK40/S+DAa8h5ziATCnlV5gxUWDsiag==">
type="hidden"
である2つの要素は、Railsで内部的に用いられるために自動で追加される要素です。以下の役割を持ちます。
- Webブラウザが正しい文字コードを送信できるようにする
- HTTPリクエストの内容の
utf8
属性の値に✓
(チェックマーク ✓)を設定する
- HTTPリクエストの内容の
- データベースに対する攻撃を防ぐためのトークンを送出する
submitで生成されるHTTPリクエスト
フォームのsubmitボタンが押されると、フォームの入力内容からHTTPリクエストが生成されます。今回のフォームHTMLにおいては、生成されるHTTPリクエストは、以下のような特徴を持ちます。
- 生成されるHTTPリクエストの種類は
POST
-
form
要素のmethod
属性で定義される - RESTfulアーキテクチャを前提とした場合、何らかの新規オブジェクトが生成されることが期待される
-
- HTTPリクエストの本体(body)内にクエリ文字列が格納される
クエリ文字列とは
- クエリ文字列は、1つ以上のクエリパラメータで構成される
- 複数のクエリパラメータがある場合、
&
でつなぐ
- 複数のクエリパラメータがある場合、
- クエリパラメータは
名前=値
という文字列である - クエリパラメータの名前はフォーム各部品要素のタグの
name
属性によって与えられる -
input
要素の場合、クエリパラメータの値はinput
タグのvalue
属性によって与えられる -
type=text
のinput
要素においては、実際に入力領域に入力された値
演習 - フォームHTML
1. Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜform
タグを使わなかったのでしょうか? 理由を考えてみてください。
考えられる理由としては以下です。
- 別のシステムに送出するためのHTTPリクエストを生成する必要がない
- 単にHTMLのチュートリアルであり、HTTPリクエストの生成を要するシステムではない
- 必要がない機能を実装すると危険性が発生する