Play Framework Form only 18 parameters

I noticed that when I add more than 18 parameters to the Form Framework Form class, I get a long (and incomprehensible to me) compilation error.

Is this a documented limitation? I need to accept up to 29 parameters in the form message. I do not decide the design and the number of parameters, because I implement the protocol from an open standard.

I draw like this:

val registration = Form(mapping(
    "client_type" -> nonEmptyText,
    "client_id" -> optional(nonEmptyText),
    ... up to 29 args, all optional(nonEmptyText)
    ){ (clientType, clientId ...) => RegistrationRequest(clientType, clientId ...) }
     { req => None })

My strategy was to make the mapping this way, and not apply / not use and create a case class hierarchy. The reason is to get around the limitation of 22 arguments in Case classes, which was the first seemingly arbitrary limit that I came across. Up to 18 args matching operations, after which I get a long compilation error.

The error message can be found here (too long to include): https://gist.github.com/2928297

I am looking for suggestions on how I can get around this limitation. I know that it is a bad design to send 29 parameters in a Post form, but it is still possible.


Hack / Bypass / Solution

Well, here is my workaround for a collaborative solution (writing this post took a lot longer than the implementation, I hacked ~ 30 minutes for it)

I wrote functions that pre-process query parameters and add a group prefix to group specific parameters. Then I use the resulting Map [String, String] and continue processing with the form class, performing validation, etc., as usual. This allows me to use the nested case classes in the mapping and fall below the limit of 18 params.

: ! , , , , , - , .

def preprocessFormParams(prefix:String, replace:String)(implicit request:Request[AnyContent]):Map[String, String] = request.body.asFormUrlEncoded.map( _.filterKeys( _.startsWith(prefix)).map( m => m._1.patch(0, replace, prefix.length)  -> m._2.head )).getOrElse(Map.empty)
def unprocessedFormParams(prefixes:Set[String])(implicit request:Request[AnyContent]):Map[String, String] = request.body.asFormUrlEncoded.map( _.filterKeys( !prefixes.contains(_) ).map( m => m._1 -> m._2.head )).getOrElse(Map.empty)

, , , , : preprocessedFormParms :

val clientParams = preprocessFormParams("client_", "client.")
("client_id" -> "val1", "client_type" -> "val2") becomes ("client.id" -> "val1", "client.type" -> "val2")

group.key1, group.key2, case

Form(mapping("client" -> mapping("type" -> nonEmptyText
    "id" -> optional(nonEmptyText),
    "secret" -> optional(nonEmptyText))
    (RegisterClient.apply)(RegisterClient.unapply)
    ... more params ...)
    (RegisterRequest.apply)(RegisterRequest.unapply)

.

implicit request =>
val clientParams = preprocessFormParams("client_", "client.")       
val applicationParams = preprocessFormParams("application_", "application.")
val unprocessedParams = unprocessedFormParams(Set("client_", "application_"))
val processedForm = clientParams ++ applicationParams ++ unprocessedParams

, , I, , , case .

clientRegistrationForm.bind(processedForm).fold( ... )

, . , , , , .

+5
3

I .

, , Play Devs.

( , , 19, 20, 21 22 [T])

, Play; , , > .

+3

mapping , . : , . a ObjectMapping1. ObjectMapping2.

ObjectMappingX ObjectMapping18, . Play play/api/data/Forms.scala

- . , , Play, ObjectMappingX .

+4

S.O. , , , , , .

import play.api.data.Form
import play.api.data.Forms._

case class P1_18(f1: String,f2: String,f3: String,f4: String,f5: String,f6: String,f7: String,f8: String,f9: String,f10: String,f11: String,f12: String,f13: String,f14: String,f15: String,f16: String,f17: String,f18: String)

case class P2_18(f1: String,f2: String,f3: String,f4: String,f5: String,f6: String,f7: String,f8: String,f9: String,f10: String,f11: String,f12: String,f13: String,f14: String,f15: String,f16: String,f17: String,f18: String)

case class P36(f1: String,f2: String,f3: String,f4: String,f5: String,f6: String,f7: String,f8: String,f9: String,f10: String,f11: String,f12: String,f13: String,f14: String,f15: String,f16: String,f17: String,f18: String,f19: String,f20: String,f21: String,f22: String,f23: String,f24: String,f25: String,f26: String,f27: String,f28: String,f29: String,f30: String,f31: String,f32: String,f33: String,f34: String,f35: String,f36: String)

P36 - , , P1/P2 - , , , .

, :

val f = Form(
    mapping(
    "" -> mapping(
        "f1" -> text,
        "f2" -> text,
        "f3" -> text,
        "f4" -> text,
        "f5" -> text,
        "f6" -> text,
        "f7" -> text,
        "f8" -> text,
        "f9" -> text,
        "f10" -> text,
        "f11" -> text,
        "f12" -> text,
        "f13" -> text,
        "f14" -> text,
        "f15" -> text,
        "f16" -> text,
        "f17" -> text,
        "f18" -> text
    )(P1_18.apply)(P1_18.unapply),
    "" -> mapping(
        "f19" -> text,
        "f20" -> text,
        "f21" -> text,
        "f22" -> text,
        "f23" -> text,
        "f24" -> text,
        "f25" -> text,
        "f26" -> text,
        "f27" -> text,
        "f28" -> text,
        "f29" -> text,
        "f30" -> text,
        "f31" -> text,
        "f32" -> text,
        "f33" -> text,
        "f34" -> text,
        "f35" -> text,
        "f36" -> text
    )(P2_18.apply)(P2_18.unapply)
    )(
    (p1, p2) =>
        P36(
            f1 = p1.f1,
            f2 = p1.f2,
            f3 = p1.f3,
            f4 = p1.f4,
            f5 = p1.f5,
            f6 = p1.f6,
            f7 = p1.f7,
            f8 = p1.f8,
            f9 = p1.f9,
            f10 = p1.f10,
            f11 = p1.f11,
            f12 = p1.f12,
            f13 = p1.f13,
            f14 = p1.f14,
            f15 = p1.f15,
            f16 = p1.f16,
            f17 = p1.f17,
            f18 = p1.f18,
            f19 = p2.f1,
            f20 = p2.f2,
            f21 = p2.f3,
            f22 = p2.f4,
            f23 = p2.f5,
            f24 = p2.f6,
            f25 = p2.f7,
            f26 = p2.f8,
            f27 = p2.f9,
            f28 = p2.f10,
            f29 = p2.f11,
            f30 = p2.f12,
            f31 = p2.f13,
            f32 = p2.f14,
            f33 = p2.f15,
            f34 = p2.f16,
            f35 = p2.f17,
            f36 = p2.f18
        )
    )(
        p => {
            val p1 = P1_18(p.f1,p.f2,p.f3,p.f4,p.f5,p.f6,p.f7,p.f8,p.f9,p.f10,p.f11,p.f12,p.f13,p.f14,p.f15,p.f16,p.f17,p.f18)
            val p2 = P2_18(p.f19,p.f20,p.f21,p.f22,p.f23,p.f24,p.f25,p.f26,p.f27,p.f28,p.f29,p.f30,p.f31,p.f32,p.f33,p.f34,p.f35,p.f36)
            Option(
                (p1,p2)
            )
        }
    )
)

: Huh. , , . ? :

val dataSeq = for(i <- 1 to 36) yield s"f${i}" -> s"text no. #${i}"
val filledFormFromMap = f.bind(dataSeq.toMap)

filledFormFromMap.value

// res9: Option[P36] = Some(P36(text no. #1,text no. #2,text no. #3,text no. #4,text no. #5,text no. #6,text no. #7,text no. #8,text no. #9,text no. #10,text no. #11,text no. #12,text no. #13,text no. #14,text no. #15,text no. #16,text no. #17,text no. #18,text no. #19,text no. #20,text no. #21,text no. #22,text no. #23,text no. #24,text no. #25,text no. #26,text no. #27,text no. #28,text no. #29,text no. #30,text no. #31,text no. #32,text no. #33,text no. #34,text no. #35,text no. #36))

. 18 , 18 , . , ObjectMapping source, , key ObjectMapping . , :

val field1 = f1._2.withPrefix(f1._1).withPrefix(key)

, "" - . , , , ObjectMapping 2,

  val field1 = f1._2.withPrefix(f1._1).withPrefix(key)

  val field2 = f2._2.withPrefix(f2._1).withPrefix(key)

mappings Mapping a Seq[Mapping], , -, , , " , , , , , field.nested.thing, , . , , , (, , ), - , 18 , , apply unapplyto combine things (as opposed to trying to use P36.apply and P36.unapply, as they will not work due to the restriction of the tuple that I consider)

0
source

All Articles