1

我有这些案例课

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_idBlog从 db 加载的对象,如果它不存在则显示错误。
播放框架文档上的示例没有帮助。
它们仅显示表示单个对象及其所有字段的表单的映射。
在这里,我代表 a 的元组,(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”将像其他映射一样工作,它将发布的数据绑定到一个对象,在我们的例子中是从 db 加载的博客,如果它不成功,它将提供一个我们可以在formWithErrors =>子句中使用的错误。

我不知道如何做到这一点,这里的文档有点缺乏......
感谢任何帮助!

4

3 回答 3

3

我最终查看了 playframwork 当前绑定的外观并实现了类似的东西,但是对于博客:

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]
于 2013-04-11T09:14:25.137 回答
2

对我来说,这看起来并不像一个绑定问题。

问题在于模型-视图-控制器拆分。绑定是一个控制器活动,它是关于将 Web 数据(来自您的视图)绑定到您的数据模型(供模型使用)。另一方面,查询数据将由模型处理。

因此,执行此操作的标准方法如下所示:

// 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 子类。在某些情况下,这可能是一个好主意,但我仍然推荐第一种方法。

于 2013-04-09T16:44:41.000 回答
1

您可以在表单定义中完成所有操作。

我从您的示例中制作了一些简单的 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
      }
    )
  }
}
于 2013-04-10T14:09:00.867 回答