Validation scala中的条件映射

Validation scala中的条件映射,validation,scala,playframework-2.0,Validation,Scala,Playframework 2.0,使用play 2.x,我有以下表单映射: val relocationDtoForm: Form[RelocationDto] = Form( mapping( "_type" -> text, "sourceToken" -> text, "exchange" -> optional(jodaDate("dd/MM/yyyy")), "completion" -> optional(jodaDate("dd/MM/yyyy")),

使用play 2.x,我有以下表单映射:

val relocationDtoForm: Form[RelocationDto] = Form(
  mapping(
    "_type" -> text,
    "sourceToken" -> text,
    "exchange" -> optional(jodaDate("dd/MM/yyyy")),
    "completion" -> optional(jodaDate("dd/MM/yyyy")),
    "expectedMoveIn" -> optional(jodaDate("dd/MM/yyyy"))
  )(RelocationDto.apply)(RelocationDto.unapply)
)
我想添加验证,以便如果_type==“sale”,则exchange是必填字段,但如果_type==“let”,则ExpectedMovien是必填字段。我似乎找不到一种使用标准播放验证程序的方法,有什么方法可以做到这一点吗

干杯
Nic

您可以使用
映射的
验证
方法。这将允许您在表单成功绑定到类后创建约束,您可以在该类中访问其他字段。它接受错误消息和布尔函数,当
false
时,该函数将向表单中添加错误

val relocationDtoForm: Form[RelocationDto] = Form(
  mapping(
    "_type" -> text,
    "sourceToken" -> text,
    "exchange" -> optional(jodaDate("dd/MM/yyyy")),
    "completion" -> optional(jodaDate("dd/MM/yyyy")),
    "expectedMoveIn" -> optional(jodaDate("dd/MM/yyyy"))
  )(RelocationDto.apply)(RelocationDto.unapply)
  .verifying(
    "Exchange is required for sales.",
     relocationRto => if(relocationRto._type == "sale") relocationRto.isDefined else true
  ).verifying(
    "Expected move in is required for let?",
     relocationRto => if(relocationRto._type == "let") expectedMoveIn.isDefined else true
  )
)

我最终的解决方案是:

  def save(id: String) = Action { implicit request =>

    //Runs some extra validation based on the _type value
    def typeSpecificValidate(params: Map[String,Seq[String]]): Seq[FormError] = params("_type") match {
      case Seq("sale") => {
        Forms.tuple(
          "exchange" -> jodaDate("dd/MM/yyyy"),
          "completion" -> jodaDate("dd/MM/yyyy")
        ).bind(params.mapValues(seq => seq.head)) match {
          case Left(errors) => errors    //Bind returns a Left(List(FormErrors)) or a Right(boundTuple)
          case _ => Nil
        }
      }
      case Seq("let") => {
        Forms.tuple(
          "expectedMoveIn" -> jodaDate("dd/MM/yyyy")
        ).bind(params.mapValues(seq => seq.head)) match {
          case Left(errors) => errors    //Bind returns a Left(List(FormErrors)) or a Right(boundTuple)
          case _ => Nil
        }
      }
    }

    val extraErrors = typeSpecificValidate(request.body.asFormUrlEncoded.get)

    Logger.debug("Custom validator found: " + extraErrors)

    val ff = relocationDtoForm.bindFromRequest
    ff.copy(errors = ff.errors ++ extraErrors).fold(
      formWithErrors => {
        formWithErrors.errors.foreach(e => Logger.debug(e.toString))
        BadRequest(views.html.relocations.detail(id, formWithErrors))
      },
      relocationDto => {
        Logger.debug(relocationDto.toString)
        Ok(views.html.relocations.detail(id, relocationDtoForm.fill(relocationDto)))
      }
    )
  }

我曾为英国税务海关总署(英国)工作,看起来HMRC已经公开了一个非常有用且易于使用的库,专门用于条件播放映射:

啊,是的,我尝试过,但问题是,由此产生的错误不会局限于字段,只会在表单级别。我需要在随后向用户演示时将该字段标记为无效。有可能吗?对于表单映射,情况似乎并非如此。表单字段在绑定到某个对象之前彼此不了解任何信息。当验证依赖于多个类似的字段时,错误真的应该绑定到一个字段吗?如果您真的想这样做,那么看起来您必须在表单之外验证您的对象(一旦绑定),并使用
withError
自己添加错误。我需要这样做的原因是我的表单和dto是多用途的,并且映射到两个案例类中的一个。在我尝试这样做之前,我需要验证它是否映射为ok(基于_类型)。因此,我认为有必要这样做,正如您所建议的,尽管在控制器中使用手动验证看起来是目前的做法。谢谢NFV@nfvindaloo您可以使用LimbGroup的验证机制并将错误绑定到表单元素。我已经在这里的另一个问题中回答了这个问题: