2013-10-27 27 views
6

我是全新的Play !,我正尝试将我现有的网站从cakePHP迁移到Play !.如何使用Play正确验证表单!在斯卡拉?

我面临的问题是关于表单验证。

我定义的情况下类用户,代表我的网站的用户:

case class User(
    val id: Long, 
    val username: String, 
    val password: String, 
    val email: String 
    val created: Date) 

(有一些更多的领域,但这些那些足以说明我的问题)

我想我的用户能够使用表格在我的网站上创建帐户,我希望通过Play!验证此表单。

所以,我创建了以下行动:

def register = Action { 
    implicit request => 

    val userForm = Form(
     mapping(
     "id" -> longNumber, 
     "username" -> nonEmptyText(8), 
     "password" -> nonEmptyText(5), 
     "email" -> email, 
     "created" -> date)(User.apply)(User.unapply)) 

    val processedForm = userForm.bindFromRequest 
    processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => { 
     Ok("Account registered.") 
    }) 
} 

很显然,我不希望用户填写ID或形式由自己创建日期。所以我的问题是:我该怎么办?

我应该在表单中定义一个新的只包含实际提供给用户的字段的“转换模型”,并将这个中间模型转换为完整的模型,然后再将其插入到我的数据库中?

也就是说,通过类似的东西取代我的行动:

def register = Action { 
    implicit request => 

    case class UserRegister(
     username: String, 
     password: String, 
     email: String) 

    val userForm = Form(
     mapping(
     "username" -> nonEmptyText(8), 
     "password" -> nonEmptyText(8), 
     "email" -> email)(UserRegister.apply)(UserRegister.unapply) 

    val processedForm = userForm.bindFromRequest 
    processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => { 
     val user = User(nextID, success.username, success.password, success.email, new Date()) 
     // Register the user... 
     Ok("Account created") 
} 

还是有另一种,更清洁的方式做我想做什么?

我已经经历了很多教程和“Play for Scala”一书,但是在我发现的唯一示例中,模型完全被表格填充......我非常喜欢Play!到目前为止,但它看起来像文件往往缺乏实例...

非常感谢您的答案!

+1

我不使用任何你描述的转换模型。因此,对于用户注册表单的示例,id字段可以是任何内容,因为我的User.create函数无论如何都会忽略它。 (在数据库中自动递增)。我使用我的用户编辑表单(与创建表单的形式相同),除了使用我的身份验证系统在表单绑定之后验证ID之外。 –

回答

4

您有几种选择:

首先,你可以使idcreated领域分别Option[Long]Option[Date]。然后使用类似的映射:

val userForm = Form(
    mapping(
    "id" -> optional(longNumber), 
    "username" -> nonEmptyText(8), 
    "password" -> nonEmptyText(5), 
    "email" -> email, 
    "created" -> optional(date) 
)(User.apply)(User.unapply) 
) 

那会,我想,是合乎逻辑的,因为UserNone ID会表明它尚未保存。当你想使用相同的表单映射来更新现有的记录时,这很有效。

或者,你可以使用ignored映射了一些任意的占位符数据:

val userForm = Form(
    mapping(
    "id" -> ignored(-1L), 
    "username" -> nonEmptyText(8), 
    "password" -> nonEmptyText(5), 
    "email" -> email, 
    "created" -> ignored(new Date) 
)(User.apply)(User.unapply) 
) 

该重用更新操作表单时,也不是那么好!

最后,别忘了你的表单映射是绑定/填充的函数,它们分别将一个元组和一个元组变为一个元组。使用案例类User.apply和​​方法只是一个方便的约定,因为这些方法就是这样做的。你可以写你的User对象的替代工厂的方法来处理表单实例:

object User { 
    def formApply(username: String, password: String, email: String): User = 
    new User(-1L, username, password, email, new Date) 

    def formUnapply(user: User): Option[(String,String,String)] = 
    Some((user.username, user.password, user.email)) 
} 

然后用户那些在窗体对象:

val userForm = Form(
    mapping(
    "username" -> nonEmptyText(8), 
    "password" -> nonEmptyText(5), 
    "email" -> email 
)(User.formApply)(User.formUnapply) 
) 

此外,值得注意的是,Scala的表格单据约为在2.2.1中变得更好(实际上它可能已经推出了here)。

+0

非常感谢您提供非常详细的答案!我特别喜欢使用apply/unapply函数的解决方案。有一些类似于应用的功能是不好的做法,还是我仍然缺少一些东西? (通过一些功能,我的意思是2到3 ...) – Martin

+0

@Martin:不知道为什么它会是不好的做法。 [companion object](http://daily-scala.blogspot.co.uk/2009/09/companion-object.html)中以不同方式构造类的实例的工厂方法是完全有效的。 – Mikesname