Railsチュートリアル 第7章 ユーザー登録 - ユーザー登録フォーム

# As-Is;To-Be

ユーザー登録機能に関し、現時点の状態をまとめると以下の通りになります。

* ユーザープロフィールページがひとまず形になった
* ユーザー登録フォームは未だ手つかず

すなわち、「/users/new ないしは/signup というページにスタブのみが存在しており、Webブラウザを使ったユーザー登録がまだできない」という状況です。

この状態から、「[Railsチュートリアルで示されているユーザー登録フォームのモックアップ](https://railstutorial.jp/chapters/images/figures/signup_mockup_bootstrap.png)のようなユーザー登録フォームを完成させ、Webブラウザを使ってユーザー登録できるようにする」というところまでがこの文章の範囲です。

# `forms_for`を使用する

## Usersコントローラの`new`アクションに`@user`変数を紐付ける

過去の学習の中で、`config/routes.rb`には、既に「/signup を、Usersコントローラの`new`アクションに紐付ける」というルーティングを定義していました。次に必要となるのは、「Usersコントローラの`new`アクションを、User（モデル）オブジェクトのインスタンスである`@user`に紐付ける」という手順です。

変更対象のコードは`app/controllers/users_controller.rb`です。以下の変更を行っていきます。

```diff: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」の項で内容を詳しく見ていきます。

```erb: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.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の追加は以下の通りです。

```diff:app/assets/stylesheets/custom.scss
...略
+
+ /* forms */
+
+ input, textarea, select, .uneditable-input {
+     border: 1px solid #bbb;
+     width: 100%;
+     margin-bottom: 15px;
+     @include box_sizing;
+ }
+
+ input {
+     height: auto !important;
+ }
...略
```

この段階で、ユーザー登録フォームをWebブラウザで表示した結果は以下のとおりです。それらしいフォームが出来上がっていますね。

<img width="1160" alt="スクリーンショット 2019-10-17 12.45.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/e9e28dfa-e345-e8f7-d3d6-57441ade2ff5.png">

# 演習 - form_forを使用する

## 1. 試しに、[リスト 7.15](https://railstutorial.jp/chapters/sign_up?version=5.1#code-signup_form)にある`:name`を`:nome`に置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?

`app/views/users/new.html.erb`を、以下のように変更します。

```diff: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`を、以下のように変更します。

```erb: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`は、その長さからして、メソッドスコープの生存期間を持っていることが期待されるであろう

このあたりのルール設定については、名著として名高い[リーダブルコード](https://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89-%E2%80%95%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-Theory-practice-Boswell/dp/4873115655)が参考になるかと思います。

# フォームHTML

## フォームの内容を定義するためのブロック

フォームHTMLの根幹となるのは、`form_for`から`end`までのブロックです。

```erb:
<%= form_for(@user) do |f| %>
...略
<% end %>
```
重要なのは以下のポイントです。

* `@user`をフォームの処理対象のデータとする
* 1つの変数をとるブロックである
    * 変数（イテレータ）オブジェクトの名前は`f`である
* `f`オブジェクトには、HTMLフォームの様々な要素を返すメソッドが定義されている
    * `label`や`text_field`、`password_field`など

## ブロック内でフォームの諸アイテムを定義する

### ラベルと入力領域の組

```erb:
<%= f.label :name %>
<%= f.text_field :name %>
```

例えば上記のコードは、Userモデルの`name`属性を設定するために用いる、ラベルと（単一行）テキスト入力領域の組を生成します。上記のコードに対応するHTMLは以下の通りです。

```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`属性

```html:
<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`要素そのもの

```html:
<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に対応します。

```erb:
<%= form_for(@user) do |f| %>
<% end %>
```

また、Usersコントローラの`app/views/users/new.html.erb`に対応する部分は下記になります。

```ruby:
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なアーキテクチャとして正しい挙動をする
* `form`要素の`action`属性は`/users`である
    * このフォームでsubmitされたときの処理は、「`/users`に`POST`リクエストを送出する」というものになる

### Railsで内部的に用いられるために自動で追加される要素

```
<input name="utf8" type="hidden" value="&#x2713;">
<input type="hidden" name="authenticity_token" value="wwiu2NvcdY59JxI/Eq96qLM88Oe8oAsBPMM7RQ1JT4xO47Pt+wSscw8fK40/S+DAa8h5ziATCnlV5gxUWDsiag==">
```

`type="hidden"`である2つの要素は、Railsで内部的に用いられるために自動で追加される要素です。以下の役割を持ちます。

* Webブラウザが正しい文字コードを送信できるようにする
    * HTTPリクエストの内容の`utf8`属性の値に`&#x2713;`(チェックマーク ✓)を設定する
* データベースに対する攻撃を防ぐためのトークンを送出する

### 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](https://www.learnenough.com/html-tutorial/html_intro)ではHTMLをすべて手動で書き起こしていますが、なぜ`form`タグを使わなかったのでしょうか? 理由を考えてみてください。

考えられる理由としては以下です。

* 別のシステムに送出するためのHTTPリクエストを生成する必要がない
    * 単にHTMLのチュートリアルであり、HTTPリクエストの生成を要するシステムではない
* 必要がない機能を実装すると危険性が発生する