バリデーションの定義
Formオブジェクト生成の定義に記述する。
Form{ mapping( "password" -> nonEmptyText.verifying(passwordCheckConstraint), "mail" -> email, "tel" -> nonEmptyText(maxLength = 13, minLength = 10).verifying(error="不正な番号です", constraint = _.matches("""[0-9-]+""")) )apply... }
のようにfield名をキーとしたMapに、Forms objectのバリデーション用field名を与えることで定義できる。
カスタムバリデーション
上記例のtelフィールドのようにverifying
でカスタムバリデーションを定義することができる。error
にエラー時のメッセージ、constraint
に入力文字列が渡されるので、Bool値を返す式を記述すればよい。
尚、verifying
メソッドはdef verifying(constraints: Constraint[T]*): Mapping[T]
とConstraint
型インスタンスを受け取ることもでき、複雑なバリデーションルールなどはConstraint
の実装として定義することができる。
上記、passwordフィールドに渡しているpasswordCheckConstraint
ルールの実装例は、公式ドキュメントを見れば理解しやすい。
Scala Custom Validations - 2.8.x
val passwordCheckConstraint: Constraint[String] = Constraint("constraints.passwordcheck")({ plainText => val errors = plainText match { case "a" => Seq(ValidationError("Password is not simple character a")) case "b" => Seq(ValidationError("Password is not simple character b")) case _ => Nil } if (errors.isEmpty) { Valid } else { Invalid(errors) } })
ValidationError
にinvalid時のエラーメッセージを定義できる。
Formsオブジェクト内包ルールのエラーメッセージのカスタム
カスタムルールについてはエラーメッセージを定義時に登録できるのだが、Formsオブジェクトに内包された標準バリデーションルールのエラーメッセージも独自のメッセージに変更したい場合がある。
まず、エラー時のFormインスタンスの状態は、以下のような状態になる。
List(FormError(password,List(error.required),ArraySeq()), FormError(tel,List(error.maxLength),ArraySeq(13)))
これは、password
フィールドがerror.required
、tel
フィールドがArraySeq(13)
のerror.maxLength
の制約違反ということを表す。実際に表示されるエラーメッセージはこの文字列からformat
メソッドを通して得られる。
よって、error.requiredに対して独自のメッセージを定義してやれば良い。
定義はconf/messages
に
error.required=入力してください
error.maxLength={0}文字まで入力可能です
とすることでformat
メソッドが返すメッセージが更新される。
Formのフィールド、エラーのDOM及び表示の整形
PlayframeworkのTwirlテンプレートエンジンは、
@helper.inputText(form("tel"))
と記述することでデフォルトのFieldConstructor
が動作し、input text
とその入力制約、入力エラー時のエラーメッセージまでDOMを出力するマクロとなっている。
大体の場合はデザインを入れ不要なDOMとなるので、整形をする必要がある。
FieldConstructorを使用して整形する
独自のFieldConstructorを定義して整形するには以下の記事が参考になる。
Play FrameworkのFormに独自のレイアウトを適用したい - YoshinoriN's Memento
Play Framework 2.7.xのフォームに独自レイアウトを適用する - たそらぼ
レンダリングView本体で整形する場合
独自のFieldConstructorを定義しない場合はTwirlに直接以下のように記述することで整形することもできる。
これを...
@helper.inputText(form("tel"))
このように記述する。
@for(item<-form.errors("tel")){ <div class="error">電話番号は@item.format</div> } <input class="input-text @if(form.errors("tel").nonEmpty){ wrn-input }" type="text" name="tel" value="@form.get.tel">
@form.get.tel
で入力された値を取得できる。
またTwirl上で特定のフィールドの特定のエラー時のみメッセージを更新したい場合は、以下のような条件指定でエラーメッセージを変更出来たりする。
@for(item<-form.errors("tel")){ @if(item.messages.head == "error.required"){ 電話番号は必ず入力してください }else{ 電話番号は@item.format } }
Formのユニットテスト
バリデートのユニットテストは、フィール名をキーとしたMap型のデータをbindFromRequestなり、bindなりすることでfoldすることができる。
あとは、エラー時のインスタンスの状態なり正常型なりをassertionすれば良い。
val p1 = Map("password"->Seq("ic"), "mail"->Seq("user@domain.com"), "tel"->Seq("1")) Account.mForm.bindFromRequest(p1).fold( hasErrors => { assert( hasErrors.errors("tel").head.message === "error.minLength" ) }, success => { } )