newアクションとルーティング
ユーザーを新規登録するため、newアクションで新しいUserオブジェクトを作成し、@user変数に入れておく。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
end
ルーティングはURLを/signupとして、usersコントローラのnewアクションに紐づけておく。
Rails.application.routes.draw do
.
.
.
get '/signup', to: 'users#new'
resources :users
end
ユーザー登録フォーム
form_forヘルパーメソッドを使って、ユーザーの新規登録フォームを作る。
form_forはUserオブジェクトを引数にとり、その属性でフォームを生成する。
ユーザー登録フォームは次のようになる。
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>
このコードにより生成されるhtmlは次のようになる。
<form accept-charset="UTF-8" action="/users" class="new_user"
id="new_user" method="post">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden"
value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />
<label for="user_email">Email</label>
<input id="user_email" name="user[email]" type="email" />
<label for="user_password">Password</label>
<input id="user_password" name="user[password]"
type="password" />
<label for="user_password_confirmation">Confirmation</label>
<input id="user_password_confirmation"
name="user[password_confirmation]" type="password" />
<input class="btn btn-primary" name="commit" type="submit"
value="Create my account" />
</form>
form_forを使ったフォームの構造
form_forの役割
form_forは変数fを使ったブロックをとる構造になっており、次のようなhtmlを生成する。
<%= form_for(@user) do |f| %>
.
.
.
<% end %>
↓
<form accept-charset="UTF-8" action="/users" class="new_user"
id="new_user" method="post">
.
.
.
</form>
action属性は"/users"、method属性は"post"となっている。
この2つの属性は、/usersに対してHTTPのPOSTリクエストを送信する、といった指示をしている。
すると、Usersリソースが提供するRESTfulなルート(https://qiita.com/kagamiya9/items/48f66b20aee03fe9da1f )に基づいて、createアクションに行き着く。
このような流れが自動でできるのは、次のような理由らしい。
①form_forの引数は@userであり、Railsは@userがUserクラスであることを認識する。
②@userはnewアクションで新規作成されているため、Railsはpostメソッドを使ってフォームを構築すべきだと判断する。
各入力フォーム
nameの入力フォームは次のようになる。
<%= f.label :name %>
<%= f.text_field :name %>
↓
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />
emailの入力フォームは次のようになる。
<%= f.label :email %>
<%= f.email_field :email %>
↓
<label for="user_email">Email</label>
<input id="user_email" name="user[email]" type="email" />
nameのtype属性は"text"だが、emailでは"email"である。
前者はf.text_field、後者はf.email_fieldとすることでtype属性がそれぞれ決まっている。
こうすると、スマホなどではメールアドレス入力用のキーボードが表示されるようになっている。
passwordの入力フォームは次のようになる。
<%= f.label :password %>
<%= f.password_field :password %>
↓
<label for="user_password">Password</label>
<input id="user_password" name="user[password]" type="password" />
type属性がpasswordの場合、フォームに文字を入力すると黒丸•で表示されるようになる。
password_confiramtionの入力フォームは次のようになる。
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
↓
<label for="user_password_confirmation">Confirmation</label>
<input id="user_password_confirmation"
name="user[password_confirmation]" type="password" />
password_confirmationでは、f.labelの第二引数に文字列"Confirmation"をとっている。
これにより、フォーム上のラベルが指定した文字列になる。
他のフォームは属性ごとに自動で設定されている。
(nameはNameになる。なお、"Confirmation"を除くと、Password confirmationになる)
サブミットボタンも同様で、value属性が任意の文字列になる。
Createアクションの作成
name属性によるハッシュの構成
<input id="user_name" name="user[name]" - - - />
<input id="user_password" name="user[password]" - - - />
inputには特殊なname属性がついている。
Railsはnameの値を使って、初期化したハッシュを (params変数経由で) 構成する。
どういうことかというと、送信された内容はまず次のようなハッシュになり、params変数に代入される。
params = { users: { name: "Foo Bar", email: "foo@invalid",
password: "foo", password_confirmation: "bar" } }
createアクションでは、このparams変数を使ってユーザーを新規登録する。
@user = User.new(params[:user])
:userシンボルの値は、入力される属性(nameやemail)とその値からなるハッシュなので、上のコードは次のコードと同じである。
@user = User.new(name: "Foo Bar", email: "foo@invalid",
password: "foo", password_confirmation: "bar")
Strong Parameters
上のようなコードはセキュリティ上問題があるらしい。
paramsをそのまま送信すると、管理者用の属性であるadminなどの値を送信して、管理者権限を奪われるからだとか。
この辺は説明が非常に分かりにくいのだが、要は許可された属性以外は送信できないようにする、ということだ。
結局のところ、次のようなuser_paramsメソッドをコントローラ内に作る。
これはStrong Parametersと呼ばれる。
先の問題があるコードは、マスアサイメントと呼ばれる。
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
# 保存の成功をここで扱う。
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
private内にあるメソッドはweb経由で外部に晒されることがない。
ユーザー登録に失敗した場合の処理
ユーザー登録に失敗した場合は、renderメソッドを使って新規登録ページに戻り、エラーメッセージを表示する。
エラー部分は長いので別記事にまとめることにする。
ユーザー登録に成功した場合の処理
成功部分も別記事にまとめることにする。