2012-08-23 50 views
18

在玩!框架,使用階,說我有一個形式,例如如下:玩!框架2.0:在使用其他字段的表單中驗證字段

import play.api.data._ 
import play.api.data.Forms._ 
import play.api.data.validation.Constraints._ 

case class User(someStringField: String, someIntField: Int) 

val userForm = Form(
    mapping(
    "someStringField" -> text, 
    "someIntField" -> number verifying(x => SomeMethodThatReceivesAnIntAndReturnsABoolean(x)) 
)(User.apply)(User.unapply) 

其中SomeMethodThatReceivesAnIntAndReturnsABoolean是執行在INT一些邏輯來驗證它的方法。

但是,我想能夠在驗證someIntField時考慮someStringField的值,有沒有辦法在遊戲框架的表單中實現這一點?我知道我可以這樣做:

val userForm = Form(
    mapping(
    "someStringField" -> text, 
    "someIntField" -> number 
)(User.apply)(User.unapply) 
.verifying(x => SomeFunctionThatReceivesAnUserAndReturnsABoolean(x)) 

然後我將整個用戶實例傳遞給驗證函數。該方法的問題在於所產生的錯誤將與整個表單相關聯,而不是與someIntField字段關聯。

有沒有辦法讓兩個東西,使用另一個字段驗證字段,並保持與我想驗證的特定字段相關的錯誤,而不是整個表單?

回答

0

我猜他們將scala-code映射到JSR-Validation。那絕對不可能。有一些論據可以做到這一點。主要是驗證應該是簡單的,而不是複雜的邏輯。我怎麼還想念這個呢。 play1的OVal對我來說更好。

+0

這很奇怪。我認爲,當另一個字段不爲空時需要某個字段是常見的事情,而不是複雜的邏輯。所以很奇怪,之前沒有人碰到過這個,特別是這個遊戲!框架創建者自己。 –

3

,如果你不介意的前綴爲你PARAMS您可以將相關的PARAMS:

val aForm = Form(
mapping(
    "prefix" -> tuple(
    "someStringField" -> text, 
    "someIntField" -> number 
) verifying (tup => your verification) 
)(tup => User.apply(tup._1, tup._2)(User.unapply...) 

我使用類似只是周圍無映射的東西。 您必須稍微調整apply/unapply並手動傳遞參數以供其編譯。

該錯誤將被註冊到「前綴」組中。

我也覺得很奇怪,你不能在你想驗證表單時使用FormError任何字段登記錯誤...

+1

此方法不起作用,因爲錯誤將與「前綴」字段關聯。我希望它與「someIntField」相關聯。儘管我可以接受使用「前綴」,但如果需要考慮「someStringField」值來驗證另一個字段,問題會再次出現。這兩個錯誤最終都會與「前綴」字段相關聯。 –

8

我有添加驗證,這取決於其他的值字段相同的要求領域。我不確定這是如何在慣用的PLAY 2.2.1中完成的,但我提出了以下解決方案。在這種用法中,我將內建「映射」降級爲簡單類型轉換器,並在「validateForm」方法中應用我的「高級內部字段」驗證。映射:

val userForm = Form(
mapping(
    "id" -> optional(longNumber), 
    "surename" -> text, 
    "forename" -> text, 
    "username" -> text, 
    "age" -> number 
)(User.apply)(User.unapply) 
) 

private def validateForm(form:Form[User]) = { 
    if(form("username").value.get == "tom" || form("age").value.get == "38") { 
    form 
     .withError("forename", "tom - forename error") 
     .withError("surename", "tom - surename error") 
    } 
    else 
    form 
} 

def update = Action { implicit request => 
    userForm.bindFromRequest.fold({ 
    formWithErrors => BadRequest(users.edit(validateForm(formWithErrors))) 
    }, { user => 
    val theForm = validateForm(userForm.fill(user)) 
    if(theForm.hasErrors) { 
     BadRequest(users.edit(theForm)) 
    } else { 
     Users.update(user) 
     Redirect(routes.UsersController.index).flashing("notice" -> s"${user.forename} updated!") 
    } 
    }) 
} 

即使它的工作原理I'm急需尋找一種更地道的版本...

編輯:在使用慣用打法定製play.api.data.format.Formatter ,更多關於http://workwithplay.com/blog/2013/07/10/advanced-forms-techniques/ - 這可以讓你以編程方式向表單添加錯誤。我格式化是這樣的:

val usernameFormatter = new Formatter[String] { 

override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] = { 
    // "data" lets you access all form data values 
    val age = data.get("age").get 
    val username = data.get("username").get 
    if(age == "66") { 
    Left(List(FormError("username", "invalid"), FormError("forename", "invalid"))) 
    } else { 
    Right(username) 
    } 
} 

override def unbind(key: String, value: String): Map[String, String] = { 
    Map(key -> value) 
} 
} 
} 

形式映射註冊這樣的:

mapping(
[...] 
    "username" -> of(usernameFormatter), 
[....] 
+1

對我來說,仍然覺得不夠習慣...我已經很長時間了這個問題了,從那以後,我一直在爲我自己的scala驗證框架工作https://github.com/NovaMage/SValidator 。這個實現幾乎完成,但目前還沒有很好的文檔。儘管它有全套的單元測試。一旦我完成了關於如何使用它以及如何將其與遊戲集成的文檔,我將在這裏發佈它作爲答案:) –

1

感謝湯姆·邁爾,在這裏我用

class MatchConstraint[A](val targetField:String, val map:(String, Map[String, String]) => A, val unmap:A => String) extends Formatter[A] { 
    override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], A] = { 
    val first = data.getOrElse(key, "") 
    val second = data.getOrElse(targetField, "") 
    if (first == "" || !first.equals(second)) { 
     Left(List(FormError(key, "Not Match!"))) 
    } 
    else { 
     Right(map(key, data)) 
    } 
    } 

    override def unbind(key: String, value: A): Map[String, String] = Map(key -> unmap(value)) 
} 

這裏什麼是我的形式看像

val registerForm = Form(
    mapping(
    "email" -> email.verifying(minLength(6)), 
    "password" -> text(minLength = 6), 
    "passwordConfirmation" -> of(new MatchConstraint[String]("password", (key, data) => data.getOrElse(key, ""), str => str)) 
)(RegisterData.apply)(RegisterData.unapply) 
) 
0

在文檔: Playframework Documentation

你可以看到下面的代碼: val userFormConstraintsAdHoc = Form( mapping( "name" -> text, "age" -> number )(UserData.apply)(UserData.unapply) verifying("Failed form constraints!", fields => fields match { case userData => validate(userData.name, userData.age).isDefined }) )

主要只是驗證不應用後,你有所有的字段映射,這樣你就可以做出更完整的驗證。

5

我相信你要找的是play.api.data.validation.Constraint

假設你有一個預定義cities列表和otherCity領域RegisterForm,你需要無論是citiesotherCity來提供,也就是說,otherCity應驗證,如果不提供cities

case class RegisterForm(
    email: String, 
    password: String, 
    cities: Option[List[String]], 
    otherCity: Option[String] 
) 

你可以寫一個自定義Constraint解決此問題:

val citiesCheckConstraint: Constraint[RegisterForm] = Constraint("constraints.citiescheck")({ 
    registerForm => 
    // you have access to all the fields in the form here and can 
    // write complex logic here 
    if (registerForm.cities.isDefined || registerForm.otherCity.isDefined) { 
     Valid 
    } else { 
     Invalid(Seq(ValidationError("City must be selected"))) 
    } 
}) 

而且你的表單定義變爲:

val registerForm = Form(
    mapping(
    "email" -> nonEmptyText.verifying(emailCheckConstraint), 
    "password" -> nonEmptyText.verifying(passwordCheckConstraint), 
    "cities" -> optional(list(text)), 
    "other_city" -> optional(text) 
)(RegisterForm.apply)(RegisterForm.unapply).verifying(citiesCheckConstraint) 
) 

在這個例子中emailCheckConstraintpasswordCheckConstraint和是我所定義類似於citiesCheckConstraint額外的自定義的約束。這在Play 2.2.x中可用。

更新: Works on Play 2.3.8以及。

+0

如何設置特定字段的錯誤呢? –