PlayframeworkのFormで要素が一つのcase classと仲良くする
要素が一つのcase classがあると困ること
例えば下のようなcase classをやり取りしたい時、JSONではちょっと困りますよね。
case class LongValue(value: Long) { require(value % 2 == 0) }
JSONの方は仕方なくこうして、
{ "longValue": { "value": 20 } }
そして Form
はこう
def hoge() = Form( "longValue" -> mapping( "value" -> longNumber.verifying(_ % 2 == 0) )(LongValue.apply)(LongValue.unapply) )
これだけならまだ良いけどこれが配列で…とかになるともう面倒ですよね。
それにせっかくcase classで require
を指定してるのに verifying
でチェックするのもなんか面倒ですよね。
専用の Formatter
を用意する
こんな感じのFormatterを用意してやるといい感じにできます。
def formatter[T](f:String => T) = new Formatter[T] { override val format = None override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], T] = try { data.get(key) match { case Some(v) => Right(f(v)) case _ => Left(Seq(FormError(key, "error.required", Nil))) } } catch { case _: Exception => Left(Seq(FormError(key, s"error.${key}", Nil))) } override def unbind(key: String, value: T): Map[String, String] = Map(key -> value.toString) } val longValue = of[LongValue](formatter(s => LongValue(s.toLong)))
これを使うとJSONはこう
{ "longValue": 20 }
Form
はこう
Form(
"longValue" -> longValue
)
LongValue
配列だったとしてもこんなので済みます。
Form(
"longValue" -> list(longValue)
)
必要であれば標準の optional
や verifying
も使えます。
Form( "longValue" -> optional(longValue.verifying(_.value <= 10)) )
さらに、他のcase classをつかいたくなったときにはこうです。
case class IntValue(value: Int) val intValue:FieldMapping[IntValue] = of[IntValue](formatter(s => IntValue(s.toInt)))
普通にマッピング書くよりすこしコード量増えてしまいますが、要素が一つのcase classが頻繁に登場するようであれば Form
はむしろスッキリするのではないでしょうか