Playframework フォームバリデーションと表示整形、ユニットテストについて

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

バリデーションの定義

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.requiredtelフィールドが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 => {
  }
)