SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

サンプルコードで学ぶRuby on Rails 5実践入門

Rails 5で実用的なユーザー登録機能を実装

サンプルコードで学ぶRuby on Rails 5実践入門 第4回

ユーザー登録時のバリデーションを追加(2)

uniquenessバリデーションの追加

 ユーザー登録は、同じメールアドレスで登録することは一般にあり得ないでしょう。そこで一意性を担保するためのバリデーションを追加します。

リスト4 app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 255 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
end

 case_sensitiveをfalseに設定することで、メールアドレスは大文字・小文字を区別しないことを表現しています。バリデーションが機能していることを確認しましょう。

$ bin/rails c

 ↓

irb(main):001:0> User.create name: 'test', email: 'test@example.com'
   (0.1ms)  BEGIN
  User Exists (38.0ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "test@example.com"], ["LIMIT", 1]]
  SQL (8.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "test"], ["email", "test@example.com"], ["created_at", 2016-12-21 10:09:19 UTC], ["updated_at", 2016-12-21 10:09:19 UTC]]
   (20.0ms)  COMMIT
=> #<User id: 2, name: "test", email: "test@example.com", created_at: "2016-12-21 10:09:19", updated_at: "2016-12-21 10:09:19">

irb(main):002:0> user = User.new name: 'test', email: 'test@example.com'
=> #<User id: nil, name: "test", email: "test@example.com", created_at: nil, updated_at: nil>

irb(main):003:0> user.valid?
  User Exists (0.7ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "test@example.com"], ["LIMIT", 1]]
=> false

irb(main):004:0> user.errors.full_messages
=> ["Email has already been taken"]
irb(main):005:0> user = User.new name: 'test', email: 'TEST@EXAMPLE.COM'
=> #<User id: nil, name: "test", email: "TEST@EXAMPLE.COM", created_at: nil, updated_at: nil>

irb(main):006:0> user.valid?
  User Exists (16.6ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "TEST@EXAMPLE.COM"], ["LIMIT", 1]]
=> false

irb(main):007:0> user.errors.full_messages
=> ["Email has already been taken"]

 メールアドレス「test@example.com」のユーザーを登録し、再度同じメールアドレスで登録しようとするとバリデーションエラーが発生している様子が分かります。また、全て大文字にした「TEST@EXAMPLE.COM」で登録しようとしても同様にバリデーションエラーが発生している様子が分かるでしょう。

emailの一意性をデータベースレイヤーで担保

 uniquenessバリデーションでemailの一意性を担保しましたが、これはあくまでアプリケーションレイヤーでの話です。データベースレイヤーでも一意性を担保する必要があります。

 そこでemailの一意性を担保するためにusersテーブルにユニークインデックスを追加しましょう。

$ bin/rails g migration add_index_to_users_email

 ↓

      invoke  active_record
      create    db/migrate/20161221101841_add_index_to_users_email.rb

 生成されたマイグレーションファイルを編集し、usersテーブルのemailカラムにユニークインデックスを追加する定義を記述します。

リスト5 db/migrate/20161221101841_add_index_to_users_email.rb
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    add_index :users, :email, unique: true
  end
end

 マイグレーションを実行しましょう。

$ bin/rails db:migrate

 ↓

== 20161221101841 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
   -> 0.1713s
== 20161221101841 AddIndexToUsersEmail: migrated (0.1714s) ====================

フォームからユーザー登録のバリデーションをテスト

 Usersリソース一覧から「New User」をクリックし、実際にフォームからバリデーションエラーが表示されることをテストしてみましょう。Name、Emailに何も入力せずに「Create User」ボタンをクリックすると以下の画面が表示されるはずです。

図1 http://localhost:3000/users
図1 http://localhost:3000/users

 presence、formatの各バリデーションエラーメッセージが表示されていることが確認できます。また、バリデーションエラーになっているName、Emailのテキストボックスが赤枠で囲まれています。

 エラーメッセージを表示するviewファイルを確認しましょう。

リスト6 app/views/users/_form.html.erb
…(中略)…
  <% if user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>

  <div class="field">
    <%= f.label :email %>
    <%= f.text_field :email %>
  </div>
…(中略)…

 viewファイルのif user.errors.any?でバリデーションエラーが発生した場合にエラーメッセージのブロックを表示する分岐を行っています。pluralizeはヘルパーメソッドで第2引数を複数形にして第1引数と文字列をスペース区切りで連結します。この場合バリデーションエラーの数が3つなので出力は画面表示の通り「3 errors」となります。

 なお、NameとEmailのテキストボックスが赤枠で表示されているのはf.labelとf.text_fieldヘルパーメソッドが、バリデーションエラー時にはdivのクラス名をfield_with_errorsで出力してくれているからです。

まとめ

 今回はscaffoldで自動生成したユーザー登録機能をより実践的に拡張しました。次回はユーザー登録時にパスワードを登録できるようにし、ログインフォームを開発します。

参考文献

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
サンプルコードで学ぶRuby on Rails 5実践入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 竹馬 力(チクバ ツトム)

WINGSプロジェクト について> 有限会社 WINGSプロジェクト が運営する、テクニカル執筆コミュニティ(代表 ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/9957 2017/02/16 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー