我有这些案例类
case class Blog(id:Long, author:User, other stuff...)
case class Comment(id:Long, blog:Blog, comment:String)
以及客户端提交数据的表单
blog_id:"5"
comment:"wasssup"
我正在编写一些简单的代码,让用户在博客中添加评论
用户已登录,因此客户端不需要他user_id
,我们知道他是谁......
我想将blog_id
绑定到从db加载的Blog
对象,如果它不存在则显示错误。
有关游戏框架文档的示例没有用处
它们仅显示表示单个对象及其所有字段的表单的映射
在这里,我代表(b:Blog, comment:String)
的元组,而Blog
我只提供它id
。
我希望有一个映射可以为我提供转换+验证+错误消息,因此我可以编写如下内容:
val form = Form(
tuple(
"blog_id" -> blogMapping,
"comment" -> nonEmptyText
)
)
form.bindFromRequest().fold(...
formWithErrors => {...
}, {
case (blog, comment) => {do some db stuff to create the comment}
...
" blogMapping" wlil像其他映射一样工作,它会将发布的数据绑定到一个对象,在我们的例子中是一个从db加载的博客,如果它不成功,它将提供我们可以在{{1}上使用的错误条款。
我不知道如何实现这一点,这里的文档有点缺乏...
任何帮助表示赞赏!
答案 0 :(得分:3)
我最后看看playframwork的当前绑定是什么样子并实现类似的东西,但对于Blog:
implicit def blogFromLongFormat: Formatter[Blog] = new Formatter[Blog] {
override val format = Some(("Blog does not exist", Nil))
def bind(key: String, data: Map[String, String]) = {
scala.util.control.Exception.allCatch[Long] either {
data.get(key).map(s => {
val blog_id = s.toLong
val blog = Daos.blogDao.retrieve(blog_id)
blog.map(Right(_)).getOrElse(Left(Seq(FormError(key, "Blog not found", Nil))))
}).get
} match {
case Right(e:Either[Seq[FormError],Blog]) => e
case Left(exception) => Left(Seq(FormError(key, "Invalid Blog Id", Nil)))
case _ => Left(Seq(FormError(key, "Error in form submission", Nil)))
}
}
def unbind(key: String, value: Blog) = Map(key -> value.id.toString)
}
val blogFromLongMapping: Mapping[Blog] = Forms.of[Blog]
答案 1 :(得分:2)
对我来说,这看起来并不像是一个绑定问题。
问题在于模型 - 视图 - 控制器拆分。绑定是一个Controller活动,它是关于将Web数据(从您的View)绑定到您的数据模型(供Model使用)。另一方面,查询数据将由模型处理。
因此,执行此操作的标准方法如下:
// Defined in the model somewhere
def lookupBlog(id: Long): Option[Blog] = ???
// Defined in your controllers
val boundForm = form.bindFromRequest()
val blogOption = boundForm.value.flatMap {
case (id, comment) => lookupBlog(id)
}
blogOption match {
case Some(blog) => ??? // If the blog is found
case None => ??? // If the blog is not found
}
但是,如果您决定在绑定中处理数据库查找(我强烈反对这一点,因为从长远来看会导致意大利面条代码),请尝试以下内容:
class BlogMapping(val key: String = "") extends Mapping[Blog] {
val constraints = Nil
val mappings = Seq(this)
def bind(data: Map[String, String]) = {
val blogOpt = for {blog <- data.get(key)
blog_id = blog.toLong
blog <- lookupBlog(blog_id)} yield blog
blogOpt match {
case Some(blog) => Right(blog)
case None => Left(Seq(FormError(key, "Blog not found")))
}
}
def unbind(blog: Blog) = (Map(key -> blog.id.toString), Nil)
def withPrefix(prefix: String) = {
new BlogMapping(prefix + key)
}
def verifying(constraints: Constraint[Blog]*) = {
WrappedMapping[Blog, Blog](this, x => x, x => x, constraints)
}
}
val blogMapping = new BlogMapping()
val newform = Form(
tuple(
"blog_id" -> blogMapping,
"comment" -> nonEmptyText
)
)
// Example usage
val newBoundForm = newform.bindFromRequest()
val newBoundBlog = newBoundForm.get
我们做的主要是创建自定义Mapping子类。在某些情况下这可能是一个好主意,但我仍然建议采用第一种方法。
答案 2 :(得分:0)
您可以在表单定义中完成所有操作。
我从你的例子中做了一些简单的scala类和对象。
models/Blog.scala
package models
/**
* @author maba, 2013-04-10
*/
case class User(id:Long)
case class Blog(id:Long, author:User)
case class Comment(id:Long, blog:Blog, comment:String)
object Blog {
def findById(id: Long): Option[Blog] = {
Some(Blog(id, User(1L)))
}
}
object Comment {
def create(comment: Comment) {
// Save to DB
}
}
controllers/Comments.scala
package controllers
import play.api.mvc.{Action, Controller}
import play.api.data.Form
import play.api.data.Forms._
import models.{Comment, Blog}
/**
* @author maba, 2013-04-10
*/
object Comments extends Controller {
val form = Form(
mapping(
"comment" -> nonEmptyText,
"blog" -> mapping(
"id" -> longNumber
)(
(blogId) => {
Blog.findById(blogId)
}
)(
(blog: Option[Blog]) => Option(blog.get.id)
).verifying("The blog does not exist.", blog => blog.isDefined)
)(
(comment, blog) => {
// blog.get is always possible since it has already been validated
Comment(1L, blog.get, comment)
}
)(
(comment: Comment) => Option(comment.comment, Some(comment.blog))
)
)
def index = Action { implicit request =>
form.bindFromRequest.fold(
formWithErrors => BadRequest,
comment => {
Comment.create(comment)
Ok
}
)
}
}