【Rails】モデルのオブジェクト生成に対する制限・制御を行ってくれる機能

##はじめに
備忘録
<br>

##目的
__データベースに不正なデータが保存されないようにすること。__

<br>

##手段
__1.データベースカラムに対して制御を記述する。__
__2.モデルに対して検証を記述する。__

##両者の違い
__データベースカラムに対する制御__
　➡︎一意性の検証

　➡︎ユーザーへエラーメッセージを届けられない

　➡︎モデルでは弾けないような、直接データベースに他のシステムから操作を加える作業に対しての制御

__モデルに対する検証__
　➡︎データベースでは制御しきれない部分の補完

　➡︎ユーザーへエラーメッセージを届ける事ができる

※今回は、制御に関する機能の説明のみです。
<br>
##1.データベースカラムへの制御でよく行われる方法4つ
※データベースカラムへの制御はマイグレーションファイルに記述していくこと。

####1.データ型を付与する。
　➡︎__データ型を付与する事で、そのデータ型以外のデータが格納されることを阻止する。__
　　→例えば、「名前カラムには文字列しか格納させたくない」場合には、名前カラムに文字列のデータ型を付与すると良い。

####2.NOT NULL制約を付与する。
　➡︎__カラムに対して、必ず何かしらの値が格納されるようにしたい場合に空の値が格納されることを阻止する。__
　　→例えば、ユーザーが新規登録する際に「名前が空のまま保存されることを阻止したい」場合には、この制約を追加すると良い。

####3.文字列の長さを制御する。
　➡︎__カラムに対して、文字列の長さを指定することで指定した文字列以上の長さのデータが格納されることを阻止する。__
　　→例えば、ユーザーが新規登録する際に「名前が20文字以上になることを阻止したい」場合には、文字列の長さを制御してあげると良い。

####4.ユニークインデックスを付与する。
　➡︎__カラムに対して、ユニークインデックスを付与する事でそのデータの一意性が確保され、同じデータが格納されることを阻止する。__
　　→例えば、ユーザーが新規登録する際に「メールアドレスが他のユーザーと被ることを阻止したい」場合には、メールアドレスカラムに対してユニークインデックスを付与してあげると良い。

>```
  コマンドを実行してモデルのクラスファイル(マイグレーションファイル)を作成
  $ rails g model User 
```

>```ruby:db/migrate/[timestamps]_create_users.rb
  class CreateUsers < ActiveRecord::Migration[5.2]
    def change
      create_table :users do |t|
        t.string :name, limit: 20, null: false
        t.string :email, null: false
　
        t.timestamps
        t.index :email, unique: true
      end
    end
  end
```

>```
  マイグレーションファイルに変更を加えたらデータベースに変更を保存する
  $ rails db:migrate
```

`t.string :name`
　➡︎データ型の付与

`limit: 20`
　➡︎文字列の長さを制御

`null: false`
　➡︎NOT NULL制約(SQLのnullとRubyのnilを混同させないこと)

`t.index :email, unique: true`
　➡︎ユニークインデックス付与で一意性確保

<br>
##2.モデルに対する検証方法は主に2つ
※モデルに対する検証はモデルファイルに記述していくこと。
※モデルに対する検証の仕組みは「検証結果が不正だった場合は、エラーメッセージを生成する」であることに注意。なので、例えばユーザーに新規登録をさせる場合は__「検証で失敗した結果、何が原因で登録できなかったのかをユーザーに知らせる」__事が目的。

####1.Railsであらかじめ用意されている、検証用のヘルパーメソッドを使う。
　➡︎__モデルに対して、validatesオプションを使って検証したいヘルパーメソッドを適用させることで、メソッドの内容に準じて検証を行う事ができる。__
　　→例えば、ユーザーが新規登録する際に「名前未入力による登録する事を阻止したい」場合には、validatesオプションに対してそれ用のヘルパーメソッドを適用させる。
　　→[その他にもたくさんの検証ヘルパーが用意されている。](https://railsguides.jp/active_record_validations.html)

####2.検証用のメソッドを自分で定義しちゃう。
　➡︎__モデルに対して、「どんな検証を行いたいか」「どんなエラーメッセージを表示させたいか」を考えてメソッドを定義していく。__
　　→例えば、ユーザーが新規登録する際「名前にハイフンを入れる事を阻止したい」ときには「名前にハイフンを含ませた場合、"名前にハイフンを入れることはできません"というエラーメッセージを生成する」といったメソッドを定義すれば良い。

　➡︎__メソッドを定義したら、validate(単数形であることに注意)オプションに適用させる。__

>```ruby:app/models/user.rb
  class User < ApplicationRecors
    validates :name, presence: true
    validate :validate_name_not_include_hyphen
　
    def validate_name_not_include_hyphen
      errors.add(:name, 'にハイフンを入れる事はできません') if name&.include?('-')
    end
  end
```

`validates :name, presence: true`
　➡︎Userモデルのname属性の値が入力されていることをチェックする。

`validate_name_not_include_hyphen`
　➡︎メソッド名。

`errors.add(:name, 'にハイフンを入れることはできません') if name&.include?('-')`
　➡︎名前にハイフンが含まれていた場合、「(名前)にハイフンを入れることはできません」というエラーメッセージを生成する。

　➡︎nameに「&」を付けているのは、名前がnilだった場合に例外を発生させないため。nilだった場合はnilを代入させることができる。そうすることでvalidatesオプションのpresence: trueで検証させる。
<br>

##※エラーメッセージを表示させるまでがゴール
エラーメッセージを生成・表示させるには(ユーザー新規登録を例にする)

1：新規登録ならユーザーコントローラのcreateアクション内で、送信されてきたデータからオブジェクトを`@user.save`メソッドで保存・更新する作業を行ってあげる。
　➡︎ビューで再利用するためにインスタンス変数でオブジェクトを定義してあげること。

2：不正なデータが送信された場合は、saveメソッドの効果により検証が実行され保存されずにfalseを返し、errorsという配列に保存に失敗したオブジェクトが格納される。
　➡︎saveメソッドやupdate_attributesメソッドなど`保存・更新時に検証を行ってくれるメソッド`を適用させないと、検証を実行してくれないので注意。
　➡︎例えば、ユーザーが名前を空のまま保存したとしたら@user.errorsの配列には「`@messages ={:name=>["を入力してください"]}`」という旨のエラーメッセージが格納される。

3：`@user.errors.full_messages`と記述することで、errors配列に格納されているエラーメッセージを取得できる。
　➡︎`名前を入力してください`

4：取得した上記のエラーメッセージをビューで表示することで、ユーザーにエラーメッセージを届ける事ができるようになる。

以上の手順を踏む必要がある。

##参考
　[現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4-ebook/dp/B07JHQ9B5T/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=%E7%8F%BE%E5%A0%B4Rails&qid=1587872306&sr=8-1)