どこがいけてない?
Railsで作ったショッピングサイトの注文フォームにメールアドレスを入力する覧があり、対応するモデルではバリデーションを次のように書いたとします。
class Item < ApplicationRecord
validates :email, presence: true, format: {with: /@/}
end
Item.create.errors.full_messages # => ["Email can't be blank", "Email is invalid"]
書式チェックが雑とかいうのは一旦置いといて、未入力だった場合、2つのエラーメッセージが出ています。これがいまいちです。
開発者からしたら些細なことかもしれません。むしろ、バリデーションがしっかり効いていることが確認できて良しと見るかもしれません。が、利用者からしてみれば煩わしく感じます。
なんでそんなに注意されないといけないのか、入力してないんだから無効なのはあたりまえじゃないかと心証を悪くしそうです。もしくはエラーメッセージの多さにびっくりして、なんだかわからないけどそんなに入力しないといけないならもういいやと帰ってしまうかもしれません。
どうすればよいか
そこで allow_blank オプションです。
次のように書式チェックの方に指定すれば、空の場合にスキップしてくれるようになります。
class Item < ApplicationRecord
validates :email, presence: true, format: {allow_blank: true, with: /@/}
end
Item.create.errors.full_messages # => ["Email can't be blank"]
Item.create(email: "xxx").errors.full_messages # => ["Email is invalid"]
これなら利用者に優しいです。
もっと良くしたい
ただ、上の場合、自分にはコードが解りにくいです。同じ行に presence と allow_blank があるからです。必須なのか空でもいいのか判然としません。(いや、わかるだろう)
また、バリデーションの部分などは、あとあと確認、変更しがちなところなので最大限に読みやすく、風通しよくしておきたいところです。
カラムのバリデーションは別に一箇所で全部書かないといけないわけではありません。なので、個人的には次のように分けて書くようにしています。
class Item < ApplicationRecord
with_options presence: true do
validates :email
end
with_options allow_blank: true do
validates :email, format: {with: /@/}
end
end
Item.create.errors.full_messages # => ["Email can't be blank"]
Item.create(email: "xxx").errors.full_messages # => ["Email is invalid"]
冗長ですか? いいんです。これぐらい冗長で。
上のブロックでは必須かどうかだけを一行ずつ並べます。下のブロックでは詳細なバリデーションに専念します。必須かどうかという情報が含まれる行と、詳細なバリデーションを分離します。
これなら開発者自信にも優しいです。
まとめ
allow_blank オプションを適切に使っていこうという内容でした。
例としてフォームのエラーメッセージを上げましたが、APIのエラーメッセージなどにも同様のことが言えそうです。詳細を返すのではなく、まず値が入力されていなければ、それだけを伝えてあげた方が親切ではないかなと思います。