ログインフォームにバリデーションをつける

#はじめに
ログインフォームにフォームオブジェクトを使ってバリデーションをつけます

#準備

とりあえず最初の設定とか準備

```
rails new login_form_validate
rails g model user email:string password_digest:string
rails db:migrate
rails g controller users index new 
rails g controller sessions new 
```

gemの設定

```
gem 'bcrypt', '~> 3.1.7'
gem 'simple_form'
gem 'bootstrap'
gem 'popper_js'
gem 'tether-rails'
gem 'jquery-rails'
gem 'validates_email_format_of'
```

routes.rb

```ruby 
Rails.application.routes.draw do
  resources :sessions
  resources :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
```

users_controller

```ruby 
class UsersController < ApplicationController

  def index 
  end 

  def new
    @user = User.new
  end

  def create 
    @user = User.create(user_params)
    if @user.save 
      redirect_to users_path 
    else 
      render :new 
    end 
  end 

  private 

  def user_params
    params.require(:user).permit(:email,:password,:password_confirmation)
  end 
end
```

users_views

```erb:new.html.erb 
<br>
<br>
<br>
<br>
<div class="container">
    <%= simple_form_for(@user) do |f| %>
        <div class="form-group">
            <%= f.input :email %>
        </div>
        <div class="form-group">
            <%= f.input :password %>
        </div>
        <div class="form-group">
            <%= f.input :password_confirmation %>
        </div>
        <%= f.submit %>
    <% end %>
</div>
```


i18nの設定

config/application.rbに

```ruby
config.i18n.default_locale = :ja
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
```

を追加

config以下にdefault,models,viewsフォルダ作成

default以下にja.yml作成し

https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

これを張り付ける

models以下にuserフォルダ作成しその下にja.yml作成し

```yml
ja:
  activerecord:
    models:
      user: ユーザー
    attributes:
      user:
        email: メールアドレス
        password: パスワード
```

viewsフォルダ以下にja.ymlを作成して

```yml 
ja: 
  helpers:
    submit:
        create: '登録する'
        update: '更新する'
        submit: '保存する'
  simple_form:
    required:
        html: '<abbr class="nec">必須</abbr>'
    labels:
      user:
        email: 'メールアドレス'
        password: 'パスワード'
    hints:
      user:
        email: 'サインインするメールアドレスを入力してください'
        password: '半角英数字のみ使えます'
    placeholders:
      user:
        email: 'メールアドレス'
        password: 'パスワード'
```

これを張り付ける

バリデーションを設定

models/user.rbに

```user.rb 

has_secure_password

    validates :email, presence: true 
    validates :email, :email_format => {:message => '正しいメールアドレスを入力してください'}
    validates :password, presence: true 
```

これを追加


これで


![qiita-form.png](https://qiita-image-store.s3.amazonaws.com/0/207921/0c54d424-bcdb-8ede-28ba-271aa2071941.png)



こんな感じになってとりあえずuserを登録できるはず


#本題

formObjectを使ってログインフォームにバリデーションを追加します

まずappいかにformsフォルダを作成その下に

session.rbを作成します


そして中身を

```ruby:session.rb 

class Session
    include ActiveModel::Model 

    attr_accessor :email, :password 

    validates :email, presence: true 
    validates :password, presence: true 

    def save 
        return false if invalid?
        true
    end 
end

```


これを追加。

まずActiveModel::Modelをインクルードしていることでバリデーションとか使える。

attr_accessorは必要で使うカラムを書いておく。

今回の場合はemailとpassword

saveに関しては.saveされるときに呼び出されてもしvalidatesに引っかかった場合はfalseがかえりそうでなければtrueがかえる。

この場合明示的にtrueを書いておかなきゃだめ。

次にsessions_controller

```ruby 
class SessionsController < ApplicationController
  def new
    @login = Session.new
  end

  def create 
    @login = Session.new(email: params[:session][:email], password: params[:session][:password])
    if @login.save && (@user = User.find_by(email: @login.email)) && @user.authenticate(@login.password)
      redirect_to users_path
    else 
      render :new 
    end 
  end 
end

```

こんな感じになる。

先ほど作ったsessionクラスを使っている。

ちなみに基本的にform_objectのクラスの名前は何でも大丈夫ですが今回はsessionにしているのには理由があってこうすることでform_forの時にsessionというクラス名からsession_pathが自動的に作られるからです。

例えばloginというクラス名のform_objectを作ってしまうとログインフォームで

```erb

<%= simple_form_for(@login) do |f| %>

<% end %>
```

としたときに自動生成されるパスはlogin_path method: :postつまり/logins/createになるからです。


```ruby 
if @login.save && (@user = User.find_by(email: @login.email)) && @user.authenticate(@login.password)
```

ここでfalseになるとrender :newが実行されます。

この時にちゃんとエラーメッセージが表示されます。

ちなみこのままだとエラーメッセージが英語になっていると思います。


これを英語にするためには

locals/models以下にsessionフォルダを作成し以下にja.ymlを作成

中身を

```yml

ja:
  activemodel:
    attributes:
        session:
            email: メールアドレス
            password: パスワード
```

とすれば日本語になってくれます。

#カスタムバリデーション

form_objectにカスタムバリデーションを追加します

emailが実在しなかったらemailは使われていないとエラーを出し、emailは存在するものだがパスワードが存在しないものの場合はemailではエラーを出さずpasswordのところでエラーを出すようにします。

コードはこんな感じになります。

```ruby 
class Session
    include ActiveModel::Model 

    attr_accessor :email, :password 

    validates :email, presence: true 
    validates :password, presence: true 
    validate  :true_email
    validate  :true_password


    def true_email
        errors.add(:email, "は使われていません。") unless User.find_by(email: email)
    end 

    def true_password
        @user = User.find_by(email: email)
        if @user 
            errors.add(:password, "は正しくありません。") unless @user.authenticate(password)
        else  
            errors.add(:password, "は正しくありません。")
        end 
    end 

    def save 
        return false if invalid?
        true
    end 
end
```

true_emailとtrue_passwordというバリデーションを作成しています。

エラーを発生させる場合はerrors.addで発生させます。

ちなみにerrors.add(:base, "")という書き方もできて全体のエラーとして表示したい場合に使えます。

ですがsimple_formでは表示されないので手を加える必要があります。

viewに@login.errors[:base]みたいな感じで表示することができます

終わり:sunny: