yucatio@システムエンジニア

趣味で作ったものいろいろ

Validationがごちゃごちゃしてきたら、分類して整理する : 整理編

前回の記事の続きです。

★前回の記事

yucatio.hatenablog.com

前回の記事では、validationの軸として

  • 単属性/属性間
  • 子属性の個数
  • マージ値/DB値/入力値
  • マスタ情報に値が存在するかのチェックが必要/不要、さらにマスタ情報の値を用いてvalidationが必要/不要
  • Validationするために、DBの同じテーブルの他のレコードの情報が必要/不要
  • Validationするために、現在時刻が必要/不要

を紹介しました。 これらの組み合わせを、ここでは大きく4つのステップに分類します。

目次

Step 分類表

単属性/属性間 子属性の個数 マージ値/DB値/入力値 マスタ情報 他レコード 現在時刻
Step 1 単属性 対象外 マージ値、入力値 不要 不要 どちらでも
Step 2 単属性、属性間 対象 マージ値、入力値 不要 不要 どちらでも
Step 3 単属性、属性間 対象 マージ値、DB値、入力値 どちらでも 不要 どちらでも
Step 4 単属性、属性間 対象 マージ値、DB値、入力値 どちらでも 必要 どちらでも

Step 1 : 単属性のvalidation

登録しようとする情報の、それぞれの項目だけを対象にvalidationを行います。必須項目や文字の長さ、フォーマット、数値が範囲内かをチェックします。

Step 2 : 属性間のvalidation

登録しようとする情報の、属性間でのvalidationを行います。時間の前後関係や、数値の大小、他方が存在したらもう片方が存在しなければならない、片方の値によってもう片方の正常となる範囲が違う場合などをチェックします。

Step 3 : DB値を用いたvalidation

DB値(変更前の値)が必要なvalidationを行います。状態遷移が妥当かや、値が変更されているかどうかをチェックします。

マスタ情報値が必要な情報もここでvalidateします。

Step 4 : 同テーブルの別レコードとの関係が必要なvalidation

すでに登録されている他のモデルとの関連性のvalidationを行います。期間の重複や全体の個数をチェックします。

Stepごとに分けてvalidationを記述する

Railsで書くならこんな感じです。会議室予約アプリを例にします。仕様は以前の記事を参照してください。 (attendeeだけ追加の仕様です。参加者は1人以上10人以下)

yucatio.hatenablog.com

class Meeting < ApplicationRecord
  belongs_to :meeting_room
  has_many :attendee

  # 単属性
  validates :subject, length: {maximum: 32}, presence: true
  validates :start_time, presence: true
  validates :end_time, presence: true

  # 子属性の個数
  validate :num_of_attendee

  # 属性間
  # start_timeはend_timeより前であること
  validate :start_time_should_be_before_end_time

  # DB値
  # 新規登録時、start_timeは現在時刻より後であること start_timeが変更された時、start_timeは現在時刻より後であること
  validate :start_time_should_be_after_current, if: :new_or_start_time_changed
  # DBのstart_timeが現在より前の場合は、start_timeを変更できない
  validate :start_time_cannot_be_changed
  # DBののstart_timeが現在より前の場合は、end_timeを変更できない
  validate :end_time_cannot_be_changed

  # 他レコードとの関連性
  # 同一の会議室に属するmeetingの期間は、重複してはならない
  validate :meeting_time_should_not_overlap, if: :new_or_start_or_end_time_changed

  # 略

end

この例だとvalidationが少ないのであまりメリットは感じられないですが、項目が多くなった時のことを想像してみると、分類してあった方が目的の情報を探しやすく、追加もしやすくなります。

Stepごとに、もしエラーがあればvalidationを中断する

以前に実装したJavaのプロジェクトでは以下のようにしました。

Step 1 を実行する
 ↓
この時点でvalidationエラーがあれば、validationを中断する
 ↓
Step 2 を実行する
 ↓
この時点でvalidationエラーがあれば、validationを中断する
 ↓
Step 3 を実行する
 ↓
この時点でvalidationエラーがあれば、validationを中断する
 ↓
Step 4 を実行する

見ての通り、Stepごとにエラーがあればvalidationを中断していました。 ユーザからすれば、一度に全てのエラーが表示されず、少し不親切な設計ではありますが、実装の面では例えばStep2の段階で数値範囲やformatは正しいということがわかっているので、実装の負担が軽くなります。

あとがき

validationが、数値の範囲とか文字列の長さとかだけだと楽なのですが、規模が大きいシステムになるとそうも言ってられないですね。

Railsの場合Stepごとにエラーがあればvalidationを中断するといった機能はないので、別の方法が必要そうです。いい方法があれば教えてください。

あと、DB値が必要かどうかで区切ったけど、マスタ情報が必要かで区切った方が一般的にはいいかもしれないです。