モデルに実装すべき役割
バリデーション(検証)
- フォームなどから入力されたデータが妥当かどうか評価し、適正なデータだけをデータベースに取り込むための機能
- 入力データのチェック
バリデーションの実装場所
- データベースの情報はモデルを通してテーブルへ登録されるのでモデルにするのが自然
- モデルによって検証された適正なデータだけがテーブルに登録されるべき
- ActiveRecordにはバリデーションを実装するためのメソッドが用意されている
バリデーション評価のタイミング
- データベスのテーブルを更新するメソッドを呼び出したときに自動的に実行される(create/save/update)
- save/update はバリデーションエラー時にfalseを返す
- create はエラーとなったインスタンスを返す
- これらの後ろに!をつけたメソッドは例外を返す
- バリデーション無視するときは save(validate: false) と指定できる
- valid?
- 任意のタイミングでバリデーションを評価するメソッド
- 真偽逆の値を返すのは invalid?
- データ登録前の確認フェーズなどに使える
- バリデーション評価の結果に対するエラー情報は、バリデーション評価メソッドを実行したレシーバーに組み込まれ、そのインスタンスに対するerrorメソッドで確認できる
標準バリデーションヘルパー
validates :title, presence: true
validates :description, length: { maximum: 100 }
- numericality
- 数値であるかや、数値の範囲が妥当であるかなどを検証する
- only_integer
- odd
- even
- greater_than
- less_than
- equal_to
- greater_than_or_equal_to
- less_than_or_equal_to
validates :height, numericality: { greater_than: 0 }
validates :postal_code, format: { with: /\A\d{3}-\d{4}\z/ }
- confirmation
- 2つの入力内容が等しいかを検証する
- #{attribute}_confirmation に入力された時に比較する
- 利用するためには「attr_accesor :#{attribute}_confirmation」のように設定する必要がある
validates :password, confirmation: true
- inclusion/exvlusion
- 文字列が配列に含まれている/含まれていないかを検証する
- acceptance
- チェックボックスにチェックがあるかを検証する
- attr_accesor を設定する必要がある
- uniqueness
- validates_associated
- 関連づいているモデルに対してバリデーションを一括に行う場合に使用する
class Product < ApplicationRecord
has_many :parts
validates_associated :parts
end
バリデーションオプション
validates :name, presence: true, allow_nil: true
- allow_blank
- message
- エラーメッセージを指定
- 指定しない場合はデフォルトのメッセージを表示
- on
- バリデーションの実行対象を指定「on: create」など(デフォルトでは create/save/update時に実行)
- if/unless
- 独自メソッドを作ることも可能
コールバック機能
コールバックとは
- 検証、作成、保存、更新、削除のイベントタイミングで、特定の処理を呼び出し実行させる機能
- before_validation/after_validation
- バリデーションの直前/直後に指定されたメソッドを呼び出す
before_validation :normalize_name
- before_(save/update/create/destroy)
- それぞれのイベントの直前に指定されたメソッドを呼び出す
- around_(save/update/create/destroy)
- それぞれのイベントの前後に指定されたメソッドを呼び出す
- after_(save/update/create/destroy)
- それぞれのイベントの直後に指定されたメソッドを呼び出す
- 複数のコールバックも指定できる
その他のコールバック
- after_initialize/after_found
- データリソースのインスタンス化ごと/取得するごとに指定されたメソッドを呼び出す
- after_touch
- 対象のモデルオブジェクトの touch メソッドが実行されるごとに指定されたメソッドを呼び出す
- after_create_commit/after_update_commit/after_destroy_commit
- モデルに対するデータベース変更のコミットが完了した時に指定されたメソッドを呼び出す
- after_rollback
スコープ
- スコープはモデルに対してよく利用する検索条件を用意しメソッドとして使うことができる機能
スコープの例
class モデル名 < ApplicationRecord
scope :メソッド名, -> { 検索条件 }
scope :メソッド名, -> (引数) { 検索条件 }
scope :target_area, -> (dist){ where("distance <= ?", dist) }
end
- それぞれのスコープはメソッドチェーンとして結合して利用できる
デフォルトスコープ
- モデルを呼び出す際に、あらかじめ用意されたスコープが常に実行される仕組み
default_scope { where("price <= 500") }
- デフォルトスコープを無視する際は、unscoped メソッドを使う
Product.unscoped.all
ロック機能
- lock_version というモデルに用意された属性がある
- lock_version は、モデルを通して処理されるリソースに対して同時にアクセスがあった時に、更新の整合性を確保するために利用される属性
楽観的ロック機能
- 不整合を防止するために、データを読み込んだ時のバージョンと更新する時のバージョンを比較して、異なっていた場合例外エラーを発生させる
- 更新されるたびに lock_version が1ずつ増加し更新の有無を管理できる
悲観的ロック機能
- データを取得する時点で他が読めないようにロックをかけ、完全な整合性を確保する方法