【Rails】モデルの検証の実装

#モデル検証の概要
データの内容の範囲をデータベースで定義することで、保存に制約をかけることができます。
それ以外にも、制約をモデル（Rubyコード）でチェックできたり、違反した場合のエラーメッセージを返すこともできます。データの内容のチェックは検証(Validation)と言い、RailsのモデルもRubyコードによって検証をする仕組みがあります。モデルの検証により、自由度の高いチェックとともに、エラーを伝えやすくできます。

##モデル検証の仕組み
Railsにおけるモデルの検証は「オブジェクトをDBに保存・更新する前に検証を行い、エラーがあれば登録・更新をしないで差し戻す」という仕組みです。この仕組みに対応するメソッドがsaveです。

##検証の書き方
検証コードは自分で記述する方法と、Railsが備えるヘルパーを利用する2通りがあります。後者でよく使われるのが以下の検証機能です。

| 検証内容                             | ヘルパーの使い方の例                                                                |
| -------------------------------- | ------------------------------------------------------------------------- |
| 必須のデータが入っているか？                   | validates :foo, presence: true                                            |
| 数値以外が入っていないか？小数点の有無・正負が期待通りか？    | validates :foo, numericality: true                                        |
| 数値の範囲が期待通りか？                     | validates :foo, inclusion: { in: 0..9}                                    |
| 文字列の長さが想定どおりか？                   | validates :foo, length: { maximum: 30}                                    |
| 文字列のフォーマットや構成文字種が想定通りか？          | validates :foo, format: { with: ・・・} validates :foo, inclusion: {in: ・・・} |
| データが一意になっているか？                   | validates :foo, uniqueness: true                                          |
| パスワードやメールアドレスが、その確認用の入力と一致しているか？ | validates :foo, confirmation                                              |

##モデルへの検証の追加(必須かどうかの検証)
作成したアプリのPostモデルのcontent属性の値が入っていなければ検証エラーになるように、検証を追加します。

```ruby:app/models/post.rb
class Post < ApplicationRecord
  validates :content, presence: true
end
```

##コントローラーの変更
次に、Postモデルが検証に引っかかった際にユーザーにわかりやすく表示し際入力を促す必要があります。まずは、コントローラーに変更を加えます。以前のcreateアクションを…

```ruby:app/controllers/posts_controller.rb
  def create
    post = Post.new(post_params)
    post.save!
    redirect_to posts_url, notice: "「#{post.content}」を投稿しました。"
  end
```

次のように変更します。

```ruby:app/controllers/posts_controller.rb
  def create
    @post = Post.new(post_params)

    if @post.save
      redirect_to @post, notice: "「#{@post.content}」を投稿しました。"
    else
      render :new
    end
  end
```

投稿に用いるメソッドをsave!からsaveに変更しました。検証によりユーザーの入力次第で、投稿に失敗するするようになりました。そこで、失敗時に処理を中断してしまうsave!ではなく、例外を発生させるsaveにより戻り値を取得し、制御を変えるようにしています。
検証エラー次の処理を追加しました。検証結果がfalseだった場合、render :newによって投稿フォーム画面を再表示させ、際入力を促します。
Postオブジェクトをインスタンス変数に代入させました。エラー時に投稿画面を再表示させる際に、ビューに検証を行った現物のPostオブジェクトを渡す必要があるからです。@postをビューに伝えることで、次の2点の効果が生まれます。

1. 前回入力したままのデータがフォームに入った状態を引き継いで表示できる。
2. Postオブジェクトが抱える検証エラーの内容をユーザーに表示できる。

##ビューの変更
続いてビューの変更を行ます。
検証エラーメッセージを表示するため、form_withの上にメッセージ領域を作成します。

```ruby:app/views/posts/_form.html.slim
- if post.errors.present?
  ul#error_explanation
    - post.errors.full_messages.each do |message|
      li= message

= form_with model: post, local: true do |f|
  …
```
errors.present?で検証エラーの有無を調べ、エラーがある時にエラーメッセージを表示するようにしています。
![A6356AAF-C034-475D-B91C-72928B05CFEA_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/999125/b2bb61f0-bf7f-84a5-599c-742a483d138a.jpeg)


##モデル検証の追加(文字列の長さの検証)
DBの定義でcontentカラムの文字列を50文字以下に制限したので、ユーザーにわかるよう検証エラーを実装します。
validatesは、同じ属性に対して複数の検証を1行に書くことができます。

```ruby:app/models/post.rb
class Post < ApplicationRecord
  validates :content, presence: true, length: { maximum: 50 }
end
```
![61B73BD0-681C-4962-9C5F-0BF2E440D65E_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/999125/65704a7c-bd93-9531-ecc1-38175150a68d.jpeg)

#参考
[現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/gp/product/B07JHQ9B5T/ref=ppx_yo_dt_b_d_asin_title_o03?ie=UTF8&psc=1)