How to get child record (case class) ID in Play Framework (2.4.0) using Forms

你说的曾经没有我的故事 提交于 2020-01-17 03:44:31

问题


I'm afraid I'm not seeing it yet.

There must be a way, using the tools as they are intended to be used, to get the ID of a child record (many-to-one typical relational database stuff) of a series of "items"

I can create a "form" (mapping) in the case class of the parent no problem.

lazy val aForm = Form( mapping( "ID" -> ignored(id), "firstName" -> nonEmptyText, "lastName" -> nonEmptyText, "listOfEmails" -> seq(email), "statusID" -> ignored(0l), "roleID" -> default(longNumber, roleID), "timezoneID" -> default(longNumber, timezoneID) (User.apply) (User.unapply) )

Now we can break this out a tad and have the following:

lazy val aForm = Form( mapping( "ID" -> ignored(id), "firstName" -> nonEmptyText, "lastName" -> nonEmptyText, "listOfEmails" -> mapping( "email" -> email, "userID" -> ignored(id), "emailTypeID" -> longNumber) (UserEmail.apply)(UserEmail.unapply), "statusID" -> ignored(0l), "roleID" -> default(longNumber, roleID), "timezoneID" -> default(longNumber, timezoneID) (User.apply) (User.unapply) )

The above is used with a form "helper" which ensure a minimum number of blanks are ready to go for new inputs.

And we can even get a tad more creative (DRY) and do like so:

lazy val aForm = Form( mapping( "ID" -> ignored(id), "firstName" -> nonEmptyText, "lastName" -> nonEmptyText, "listOfEmails" -> seq(UserEmail.EMPTY.form(id).mapping), "statusID" -> ignored(0l), "roleID" -> default(longNumber, roleID), "timezoneID" -> default(longNumber, timezoneID) (User.apply) (User.unapply) )

where the form inside the case class UserEmail is "reused" instead of having it "repeated" in the parent case class. EMPTY is just an object that I use as a constant for each case class.

Note that even though this creates "blank" form entries they are data filled on the form/screen. Thanks in no small part to this little helper (repeatWithIndex -- https://gist.github.com/benoit-ponsero/4484313) which everyone says isn't really needed!(???)

What I haven't been able to grok yet is how I take a Seq[UserEmail] (which of course is available inside the parent User case class) and propagate it's ID's down the food chain and back round-trip to the POST on the controller?

Currently I can do this funky thing with zip and create a doubled list and piece the bits back together again with a foreach server side but this just "feels yucky".

Depending upon the data involved (I want to do this for all kinds of entities far more complex than simple email addresses) it could get difficult to ascertain with exactitude if any given record is an add, change or delete. Even assuming it was possible this is a lot of work to implement custom work on a case by case basis (and a lot of needless(?) debt inherited to boot!)

Anyone cracked this nut yet and care to share?


回答1:


Ok - I have one firing solution.

First I've reworked the temple to include hidden fields (yuck!) for each element.

Then inside of apply I've added logic like so:

def apply( id: Long, firstName: String, lastName: String, emails: Seq[UserEmail], statusID: Long, roleID: Long, timezoneID: Long) = { trace("emails: {}", emails) emails foreach { x => x.writeDB trace(x) } new User(id, firstName, lastName, statusID, roleID, timezoneID) }

and then in the unapply method

def unapply(x: User): Option[(Long, String, String, Seq[UserEmail], Long, Long, Long)] = Some(x.id, x.firstName, x.lastName, x.tkEmails, x.statusID, x.roleID, x.timezoneID)

It appears that between these two methods the magic occurs. Notice the assymetry here in that I do not have the Seq[UserEmail] as a val in the case class constructor. It is silently dropped by the unapply and processed by the apply.

The "inner" form userEmail has to be implemented like so:

def form(userID: Long) = Form( mapping( "ID" -> default(longNumber, id), "userID" -> default(longNumber, userID), "address" -> optional(email) )(UserEmail.apply)(UserEmail.unapply) )

Wish to hell I had known that a long time ago!

This feels far too far from elegant (There is a LOT more code that I have to take into account - this is a radically stripped down example just to show the relevant concept) but at least it is workable - still would love to see something far more "play-ish" if possible!



来源:https://stackoverflow.com/questions/30651605/how-to-get-child-record-case-class-id-in-play-framework-2-4-0-using-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!