⑥ユーザを新規登録できるようにしてみよう

この章のゴール

・ユーザ登録ページを作成する。

・バリデーションの概念を理解する。

ユーザの新規登録

この章では、実際にWebページ上から新規ユーザの登録ができるようにしていきましょう。

完成イメージは、下記です。

今回の作業用に新たなブランチを切ります。

$ cd ~/hello_world/
$ git checkout -b feature/sign-up-user
Switched to a new branch 'feature/sign-up-user'

binding.pryの導入

まず初めに、binding.pryを利用するためのGemを追加します。

binding.pryをコードの中に挟むことで、処理を途中で止めることができ、その時の変数の値を確認することができます。

システム開発では、このように途中で処理を止めて、値の中身を確認しながら開発を進めていきます。

これをデバックといいます。

Gemfileを開いて、group :development, :test doの中を以下のように変更してください。

・
・
・

group :development, :test do
  gem 'pry-rails'
  gem 'pry-byebug'
  gem 'pry-doc'
end

・
・
・

 

そして、bundle installします。

$ bundle install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using rake 12.3.3
・
・
・

 

そして、サーバを再起動(「Ctrl + C」で止めて、「rails s」で起動する)してください。

デバックについて

大きいシステムを構築する際に、「この変数に何の値が入っているのかを確認したい」という際に、binding.pryなどで処理を一時的に止めて確認をします。

デバックは、開発する中で最も利用頻度の高い機能の一つなので、まずは概念を抑えておきましょう。

ユーザ新規登録ページの作成

まず初めに、ユーザ登録の処理を作成するので、Usersコントローラを作成します。

$ rails g controller users
Running via Spring preloader in process 4567
      create  app/controllers/users_controller.rb
      invoke  erb
      create    app/views/users
      invoke  test_unit
      create    test/controllers/users_controller_test.rb
      invoke  helper
      create    app/helpers/users_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss

 

また、以下のコマンドでユーザの新規登録する際のビューを作成します。

$ touch app/views/users/new.html.erb

 

次に、上で作成したnew.html.erbを開き、ユーザ登録ページの作成します。

<div class="users-new-wrapper">
  <div class="container">
    <div class="row">
      <div class="col-md-offset-4 col-md-4 users-new-container">
        <h1 class="text-center text-white">ユーザ登録</h1>
        <%= form_for @user do |f| %>
          <div class="form-group">
            <%= f.label :名前, class: 'text-white' %>
            <%= f.text_field :name, class: 'form-control' %>
          </div>
          <div class="form-group">
            <%= f.label :メールアドレス, class: 'text-white' %>
            <%= f.text_field :email, class: 'form-control' %>
          </div>
          <div class="form-group">
            <%= f.label :パスワード, class: 'text-white' %>
            <%= f.password_field :password, class: 'form-control' %>
          </div>
          <div class="form-group">
            <%= f.label :パスワード(確認用), class: 'text-white' %>
            <%= f.password_field :password_confirmation, class: 'form-control' %>
          </div>

          <%= f.submit "登録", class: 'btn-block btn-white' %>
        <% end %>
        <%= link_to 'ログインはこちら', '#',class: 'text-white' %>
      </div>
    </div>
  </div>
</div>

 

また、TOPページからユーザ登録ページへ遷移できるように変更します。

各ファイルを、以下の通りに書き換えてください。

app/views/pages/index.html.erb

<div class="container">
  <div class="row">
    <div class="login col-md-4 col-md-offset-4 text-center">
      <h3>TOPページ</h3>
      <p>以下から選択してください。</p>
      <%= link_to 'ログイン', '#', class: 'btn btn-primary btn-block btn-large' %>
      <%= link_to 'ユーザ登録', new_user_path, class: 'btn btn-default btn-block btn-large' %>
    </div>
  </div>
</div>

↑TOPページからユーザ登録ページへ遷移できるように設定しています。

 

config/routes.rb

Rails.application.routes.draw do
  get '/', to: 'pages#index'

  resources :users
end

resources :usersと記述することで、indexcreateneweditshowupdatedestroyというルーティングが全て追加されます。

ターミナル上で、以下のように入力することで、ルーティングを確認することもできます。

$ rake routes | grep users
users GET    /users(.:format)                                                                         users#index
                          POST   /users(.:format)                                                                         users#create
                 new_user GET    /users/new(.:format)                                                                     users#new
                edit_user GET    /users/:id/edit(.:format)                                                                users#edit
                     user GET    /users/:id(.:format)                                                                     users#show
                          PATCH  /users/:id(.:format)                                                                     users#update
                          PUT    /users/:id(.:format)                                                                     users#update
                          DELETE /users/:id(.:format)                                                                     users#destroy

 

app/controllers/users_controller.rb

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(name: params[:user][:name], email: params[:user][:email], password: params[:user][:password])
    if @user.save
            redirect_to '/'
    else
        render :new
    end
  end
end

 

new(ユーザ登録画面)とcreate(ユーザ作成処理)のアクションを作成しています。

 

実装が完了したら、TOPページ(http://localhost:3000/)から「ユーザ登録」をクリックして、情報を入力して登録してみます。

すると、以下のようにTOPページへ遷移します。

rails cでコンソールに入って、Userが登録されているか確認してみましょう。

 $ rails c
Running via Spring preloader in process 8997
Loading development environment (Rails 5.2.3)
[1] pry(main)> 

 

[1] pry(main)> User.all
  User Load (0.1ms)  SELECT "users".* FROM "users"
=> [#<User:0x00007fc890ace8a0
  id: 7,
  email: "wonder@gmail.com",
  password: "password",
  name: "ワンダフルコード",
  created_at: Tue, 17 Sep 2019 23:24:28 UTC +00:00,
  updated_at: Tue, 17 Sep 2019 23:24:28 UTC +00:00>]

 

Webページ上から入力した情報が登録されていることが確認できます。

ユーザ登録に対する現状の問題点

しかし、現状では以下のような問題が残されています。

[box02 title=”現状の問題点”]

① 登録に成功したのか、失敗したのかが分からない

② 既に登録されているemailでも登録ができる

③ name、emailが空でも保存ができる

④ パスワードがそのまま保存されている

[/box02]

これらを順に解説します。

① 登録に成功したのか、失敗したのかが分からない

今回は、ユーザ登録が成功した際に、TOPページに遷移するように設定していますが、単に遷移するだけでは、ユーザ登録が成功したのか、失敗したのかが分かりません。

おそらく、実際に「登録」ボタンを押した際に、TOPページへそのまま遷移され、分かりずらかったかと思います。

railsには、フラッシュメッセージという機能があり、「ユーザ登録が完了しました」などのメッセージを一度だけ表示することが可能です。

② 既に登録されているemailでも登録ができる

通常、同じEmailでログインできてしまうと、同じユーザが2つ存在することになり、問題が起きます。

なので、Emailは、一つだけ存在することを保証する必要があります。

railsでは、バリデーションという仕組みがあり、データベースへの保存時、値が重複していないかをチェックしてくれます。

③ name、emailが空でも保存ができる

現状は、Webブラウザ上のテキストフィールドから入力した情報でユーザを作成しているので、もし空の状態で「登録」ボタンを押すと、そのままデータベースに保存されてしまいます。

しかし、名前やEmailは、必須の入力項目なので、これでは問題です。

これも、②のバリデーションという仕組みで、保存する値が空でないかをチェックすることができます。

④ パスワードがそのまま保存されている

入力されたパスワードが、そのままデータベースに格納されると、仮に、SQLインジェクションという脆弱性がWebアプリケーションに潜んでいた場合、データベースの情報がそのまま攻撃者に見られてしまいます。

そのため、パスワードはそのままデータベースに保存するのではなく、ハッシュ化した値をデータベースに格納する必要があります。

対策

① 登録に成功したのか、失敗したのかが分からない

 

この問題に対応するには、以下のようにファイルを修正してください。

app/controllers/users_controller.rb

・
・
・
  def create
    @user = User.new(name: params[:user][:name], email: params[:user][:email], password: params[:user][:password])
    if @user.save
            redirect_to '/', success: '登録が完了しました'
    else
        flash.now[:danger] = "登録に失敗しました"
        render :new
    end
  end
end

↑ここでは、フラッシュメッセージを設定しています。

 

app/assets/stylesheets/custom.scss

・
・
・
.alert {
  margin-bottom: 0px;
  position: fixed;
  width: 100%;
  z-index: 10000;
}

↑フラッシュメッセージのCSSを設定しています。

 

app/views/layouts/application.html.erb

・
・
・

  <body>
    <% flash.each do |key, value| %>
      <div class="alert alert-<%= key %>" role="alert"><%= value %></div>
    <% end %>

    <%= yield %>
  </body>
</html>

↑フラッシュメッセージを表示するビューを追加しています。

application.html.erbは、全てのページで共通で読み込まれます。

 

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  add_flash_types :success, :info, :warning, :danger
end

application_controller全てのアクションが呼び出される前に、呼び出されます。

Bootstrapに対応した successinfowarningdanger 4つのキーが使用できるようになります。

これを追加しないと、フラッシュメッセージを表示できません。

 

このように設定することで、ユーザの「登録」ボタンを押した際に、画面上部にメッセージが表示されるようになります。

そして、リロードして、ページを再読み込みすると、消えることが確認できます。

 

② 既に登録されているemailでも登録ができる

app/models/user.rbを開き、下記のようにバリデーションを追加することで、重複のチェックができるようになります。

class User < ApplicationRecord
  validates :email, uniqueness: true
end

 

今度は、下記のように、既に登録しているEmailで登録をしてみます。

すると、以下の通り、登録ができない趣旨のエラーメッセージが表示されるようになります。

③ name、emailが空でも保存ができる

app/models/user.rbを開き、下記のようにバリデーションを追加することで、空のチェックを行うことができます。

class User < ApplicationRecord
  validates :email, uniqueness: true

  validates :name, presence: true
  validates :email, presence: true
end

 

そして、今度は、名前やEmailを空白にして「登録」を押すと、以下のようにエラーメッセージになることが確認できます。

④ パスワードがそのまま保存されている

 

railsでは、パスワードをハッシュ化して保存するために、「has_secure_password」というメソッドを利用します。

app/models/user.rbを開き、以下の通りに追加します。

class User < ApplicationRecord
  validates :email, uniqueness: true

  validates :name, presence: true
  validates :email, presence: true

  validates :password_confirmation, presence: true

  has_secure_password
end

 

app/controllers/users_controller.rbを開き、以下の通り、変更します。

@user = User.new(name: params[:user][:name], email: params[:user][:email], password: params[:user][:password])

↓

@user = User.new(name: params[:user][:name], email: params[:user][:email], password: params[:user][:password], password_confirmation: params[:user][:password_confirmation])

 

また、「has_secure_password」を利用する際、「has_secure_password」を追加したモデルに「password_digest」というカラムを追加する必要があります。

そのため、以下のようにmigrationファイルを生成して、カラムを追加します。

$ rails g migration add_password_digest_to_users password_digest:string
Running via Spring preloader in process 14778
      invoke  active_record
      create    db/migrate/20190918020519_add_password_digest_to_users.rb

 

次に、上記のコマンドで生成れた、db/migrate/20190918020519_add_password_digest_to_users.rbを開くと、以下のように記載されています。

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :password_digest, :string
  end
end

 

では、migrationを実行します。

$ rails db:migrate
== 20190918020519 AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
   -> 0.0024s
== 20190918020519 AddPasswordDigestToUsers: migrated (0.0029s) ================

 

また、has_secure_passwordは、ハッシュ化を実行する際に、bcryptというGemを利用するため、Gemfileを開いて、gem ‘bcrypt’を追加します。

・
・
・

gem 'bcrypt'

group :development, :test do
  gem 'pry-rails'
  gem 'pry-byebug'

・
・
・

 

そして、bundle installします。

$ bundle install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using rake 12.3.3
Using concurrent-ruby 1.1.5
Using i18n 1.6.0
・
・
・

 

そして、サーバを再起動(「Ctrl + C」で止めて、「rails s」で起動する)してください。

サーバの再起動後、新たなユーザを登録してから、rails cで作成されたユーザを確認してみましょう。

$ rails c
Running via Spring preloader in process 15333
Loading development environment (Rails 5.2.3)
[1] pry(main)> 

 

pry(main)> User.last
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<User:0x0000000005be5258
 id: 12,
 email: "wonder_code@gmail.com",
 password: nil,
 name: "wonderfulテスト",
 created_at: Wed, 18 Sep 2019 02:17:54 UTC +00:00,
 updated_at: Wed, 18 Sep 2019 02:17:54 UTC +00:00,
 password_digest: "$2a$12$C8FBZ0aXWded9dYM0mApwuH8RerrB1o2slCYfsZLZe.noOa76Jv46">

 

すると、password_digestというカラムに、暗号化されたパスワードが保存されていることを確認できます。

プルリクエストの作成

では、ここまでの内容をコミットしてpushしておきましょう。

$ cd ~/hello_world/

$ git add -A
$ git commit -m "ユーザ登録機能を実装"
[feature/sign-up-user a694842] ユーザ登録機能を実装
 Committer: EC2 Default User <ec2-user@ip-172-31-43-4.ap-northeast-1.compute.internal>
create mode 100644 hello_world_app/app/assets/javascripts/users.coffee
・
・
・
 create mode 100644 hello_world_app/app/views/users/new.html.erb
 create mode 100644 hello_world_app/db/migrate/20190918020519_add_password_digest_to_users.rb
 create mode 100644 hello_world_app/test/controllers/users_controller_test.rb

 

$ git push origin feature/sign-up-user
Counting objects: 34, done.
Compressing objects: 100% (31/31), done.
Writing objects: 100% (34/34), 4.24 KiB | 394.00 KiB/s, done.
Total 34 (delta 12), reused 0 (delta 0)
remote: Resolving deltas: 100% (12/12), completed with 12 local objects.
remote: 
remote: Create a pull request for 'feature/sign-up-user' on GitHub by visiting:
remote:      https://github.com/wakitakomure/hello_world/pull/new/feature/sign-up-user
remote: 
To github.com:wakitakomure/hello_world.git
 * [new branch]      feature/sign-up-user -> feature/sign-up-user

 

次に、githubを開いて、「Compare & pull request」をクリックします。

これで、リモートのmasterブランチにマージされました。

最後に、ローカルのmasterブランチにもプルしておきましょう。

まず、ブランチをmasterに切り替えます。

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

 

次に、プルします。

$ git pull origin master
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From github.com:wakitakomure/hello_world
 * branch            master     -> FETCH_HEAD
   533b7b7..5c60251  master     -> origin/master
Updating 533b7b7..5c60251
Fast-forward
 hello_world_app/Gemfile                                                   |  6 ++++--
 hello_world_app/Gemfile.lock                                              | 19 ++++++++++++++++++-
 hello_world_app/app/assets/javascripts/users.coffee                       |  3 +++
 hello_world_app/app/assets/stylesheets/custom.scss                        |  6 ++++++
 hello_world_app/app/assets/stylesheets/users.scss                         |  3 +++
 hello_world_app/app/controllers/application_controller.rb                 |  3 +++
 hello_world_app/app/controllers/users_controller.rb                       | 15 +++++++++++++++
 hello_world_app/app/helpers/users_helper.rb                               |  2 ++
 hello_world_app/app/models/user.rb                                        |  6 ++++++
 hello_world_app/app/views/layouts/application.html.erb                    |  4 ++++
 hello_world_app/app/views/pages/index.html.erb                            |  2 +-
 hello_world_app/app/views/users/new.html.erb                              | 30 ++++++++++++++++++++++++++++++
 hello_world_app/config/routes.rb                                          |  2 ++
 hello_world_app/db/migrate/20190918020519_add_password_digest_to_users.rb |  5 +++++
 hello_world_app/db/schema.rb                                              |  3 ++-
 hello_world_app/test/controllers/users_controller_test.rb                 |  7 +++++++
 16 files changed, 111 insertions(+), 5 deletions(-)
 create mode 100644 hello_world_app/app/assets/javascripts/users.coffee
 create mode 100644 hello_world_app/app/assets/stylesheets/users.scss
 create mode 100644 hello_world_app/app/controllers/users_controller.rb
 create mode 100644 hello_world_app/app/helpers/users_helper.rb
 create mode 100644 hello_world_app/app/views/users/new.html.erb
 create mode 100644 hello_world_app/db/migrate/20190918020519_add_password_digest_to_users.rb
 create mode 100644 hello_world_app/test/controllers/users_controller_test.rb

 

最後に、feature/sign-up-userブランチを削除しておきましょう。

$ git branch -D feature/sign-up-user
Deleted branch feature/sign-up-user (was a694842).

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA