Validate scala forms, when form contains inner case classes - forms

Help with Scala forms validation,
Here is the case class for the form data:
case class Data(
firstName: String,
lastName: String,
email: String,
confirm_email: String,
password: String,
confirm_password: String)
}
And the Scala Form:
val form = Form(
mapping(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
"email" -> email,
"confirm_email" -> email,
"password" -> nonEmptyText(minLength = 8),
"confirm_password" -> nonEmptyText(minLength = 8))(Data.apply)(Data.unapply))
Now the problem is we need to validate the "email" and "confirm" email, but the problem is we need to create tuples or mapping. And so what is the best way to handle these kinds of form validation situations. It can be easily done by only using tuples and not mapping it to any case class.
But what can be done if we are requried to use mapping and case classes in forms.

First things first, I'd get rid of the confirm_email and confirm_password fields since they're redundant in the Data model. After this operation, it'll look like this:
case class Data(
firstName: String,
lastName: String,
email: String,
password: String)
Next, your form mapping needs to be updated:
val form = Form[Data](
mapping(
"firstName" -> nonEmptyText,
"lastName" -> nonEmptyText,
"email" -> tuple(
"email1" -> email,
"email2" -> email
).verifying(Messages("form.error.emailNotEquals"), email => email._1 == email._2),
"password" -> tuple(
"pass1" -> nonEmptyText(minLength = 8),
"pass2" -> nonEmptyText(minLength = 8)
).verifying(Messages("form.error.passwordNotEquals"), password => password._1 == password._2)
)((firstName, lastName, email, password) => Data(firstName, lastName, email._1, password._1))
((form: Data) => Some((form.firstName, form.lastName, (form.email, form.email), ("", ""))))
)
Two changes are required:
Nested mapping with validation both for email and password fields.
Custom apply and unapply method implementation in order to map the form with six fields into the models case class with four fields.
Notice that the custom unapply method doesn't set values for password fields since it's a desired behaviour in virtually all cases.
Finally, your view must be altered to refer new form tuple mapping correctly. For instance, fields for email should look as follows:
#helper.inputText(dataForm("email.email1"))
#helper.inputText(dataForm("email.email2"))
Fields which don't use new tuple mappings stay unchanged.

Related

Scala, create a form with more than 22 fields

I'm using scala and Play framework and i want create a form with more than 22 fields so I share my field in 3 tuple like that:
val firstMapping = tuple(
"f1" -> text, "f2" -> text, ... "f18" -> text
)
val secondMapping = tuple(
"f19" -> text, "20"-> text ... "f25" -> text
)
val thirdMapping = tuple(
"f26" -> text, ... "f29" -> text
)
So after I regroup them in a form:
val createForm = From(tuple(
"general" -> firstMapping,
"specific" -> secondMapping,
"more_specific" -> thirdMapping
))
I think this is the good solution, but my question is about the view file (i'm in a MVC architecture)
In that view I want to pass my form like that:
#(formCreate: Form[])
But I don't know what I need to put in the " [] " (I'm french I don't know wath is the word for that in english) and how to create my field in HTML ?
Usually I use that kind html form:
#helper.form() {
<input type="text" name="id_metier" id="id_metier" maxlength="255"/>
}
So can I use that kind of field again or I need to use specific field from Play framework ? And what are the parameter for that #(formCreate: Form[]) ?
Thank you for your help
Your form is of type Tuple3 with some other tuples inside. Painful to read, write, use, maintain.
Form[((String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String), (String, String, String, String, String, String, String), (String, String, String, String))]
Refer to the docs: https://www.playframework.com/documentation/2.5.x/ScalaForms
and just create a case class that will contain 3 nested case classes for your data, name fields appropriately.
Here is the example from docs for nested case class
case class AddressData(street: String, city: String)
case class UserAddressData(name: String, address: AddressData)
val userFormNested: Form[UserAddressData] = Form(
mapping(
"name" -> text,
"address" -> mapping(
"street" -> text,
"city" -> text
)(AddressData.apply)(AddressData.unapply)
)(UserAddressData.apply)(UserAddressData.unapply)
)
When creating form you refer to nested fields with . notation
#helper.inputText(userFormNested("name"))
#helper.inputText(userFormNested("address.street"))
#helper.inputText(userFormNested("address.city"))

play framework how to reuse and extend a form mapping

I have a form mapping such as the following:
val myBaseMapping = mapping(
"email" -> email,
"password" -> text.verifying("Please provide a password", !_.isEmpty)
)(BaseModel.apply)(BaseModel.unapply)
This represents FormA. I have another form, FormB, that is identical but adds a couple more fields. This is what its mapping would look like:
val myExtendedMapping = mapping(
"email" -> email,
"password" -> text.verifying("Please provide a password", !_.isEmpty)
"name" -> text,
"website" -> text
)(ChildModel.apply)(ChildModel.unapply)
ChildModel extends BaseModel: it adds 2 new fields, name and website.
I am trying to code myExtendedMapping in such a way that I don't have to duplicate the binding definitions for the shared fields (email and password).
I am not sure what the Scala syntax would be here. I do not know how to 'extend' a given mapping and add bindings to it. Also, I'd prefer not to add ad-hoc verification because of the difference in behavior. Is this possible or do I just have to duplicate code?
What you can do is a simple composition. For example:
case class BaseModel(email: String, password: String)
case class ChildModel(name: String, website: String, base: BaseModel)
val commonMapping = mapping(
"email" -> email,
"passwod" -> texttext.verifying("Please provide a password", !_.isEmpty)
)(BaseModel.apply)(BaseModel.unapply)
val myExtendedForm = Form[ChildModel](mapping(
"name" -> text,
"website" -> text,
"base" -> commonMapping
)
((name, website, base) => ChildModel(name, website, base)) //bind
(child => Some(child.name, child.website, child.base)) //unbind
)

How to correctly validate forms with Play! in Scala?

I'm brand new to Play!, and I'm trying to migrate my existing website from cakePHP to Play!.
The problem I'm facing is about form validation.
I defined a case class User, representing the users of my website :
case class User(
val id: Long,
val username: String,
val password: String,
val email: String
val created: Date)
(There are some more fields, but these ones suffice to explain my problem)
I would like my users to be able to create an account on my website, using a form, and I would like this form to be validated by Play!.
So, I created the following action :
def register = Action {
implicit request =>
val userForm = Form(
mapping(
"id" -> longNumber,
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> date)(User.apply)(User.unapply))
val processedForm = userForm.bindFromRequest
processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => {
Ok("Account registered.")
})
}
Obviously, I do not want the user to fill the id or the creation date by himself in the form. So my question is : what should I do ?
Should I define a new "transition model" containing only the fields that are actually provided to the user in the form, and transform this intermediary model into the complete one before inserting it into my database ?
That is, replace my action by something like that :
def register = Action {
implicit request =>
case class UserRegister(
username: String,
password: String,
email: String)
val userForm = Form(
mapping(
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(8),
"email" -> email)(UserRegister.apply)(UserRegister.unapply)
val processedForm = userForm.bindFromRequest
processedForm.fold(hasErrors => BadRequest("Invalid submission"), success => {
val user = User(nextID, success.username, success.password, success.email, new Date())
// Register the user...
Ok("Account created")
}
Or is there another, cleaner way to do what I want ?
I've been through many tutorials and the book "Play for Scala", but in the only examples that I found, the models were fully filled by the forms... I really like Play! so far, but it looks like the documentation often lacks examples...
Thank you very much for your answers !
You have a few choices:
Firstly, you could make the id and created fields Option[Long] and Option[Date] respectively. Then use a mapping like:
val userForm = Form(
mapping(
"id" -> optional(longNumber),
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> optional(date)
)(User.apply)(User.unapply)
)
That would, I think, be logical, since a User with a None id would indicate that it has not yet been saved. This works well when you want to use the same form mapping to update an existing record.
Alternately, you can use ignored mappings with some arbitrary placeholder data:
val userForm = Form(
mapping(
"id" -> ignored(-1L),
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email,
"created" -> ignored(new Date)
)(User.apply)(User.unapply)
)
This is not so good when reusing the form for update operations!
Finally, don't forget that your form mappings are bound/filled by functions that turn a tuple into an object, and an object into a tuple respectively. It's just a convenient convention to use the case class User.apply and User.unapply methods since these do just that. You could write alternative factory methods on your User object to handle form instantiation:
object User {
def formApply(username: String, password: String, email: String): User =
new User(-1L, username, password, email, new Date)
def formUnapply(user: User): Option[(String,String,String)] =
Some((user.username, user.password, user.email))
}
And then user those in the Form object:
val userForm = Form(
mapping(
"username" -> nonEmptyText(8),
"password" -> nonEmptyText(5),
"email" -> email
)(User.formApply)(User.formUnapply)
)
Also, it's worth noting that the Scala forms documentation is about to get much better in 2.2.1 (and in fact it might already have rolled out here).

Play Forms from case classes

I'm new to playframework. I have my model for a User as well as the accompanying object for the static methods...
case class User(id: Int, username: String, password: String, fullname: String, /
lastLogin : Date, updatedOn: Date, updatedBy: Int, createdOn: Date, createdBy: Int)
I want to create a form for this class omitting some of the details. Currently I have a UserForm case class
case class UserForm(fullName:String, username: String, password:String, confirm:String)
To allow me to use:
val userForm = Form(
mapping(
"fullName" -> of[String],
"username" -> of[String],
"password" -> of[String],
"confirm" -> of[String]
)(UserForm.apply)(UserForm.unapply)
)
This feels kind of Hacker-ish. Is there an idiomatic and more conscice way to do this?
How about
val userForm = Form(
mapping(
"fullName" -> text,
"username" -> text,
"password" -> text,
"confirm" -> text
)(UserForm.apply)(UserForm.unapply)
)
There are a lot more built-in checks and validations. The basics are listed here: http://www.playframework.com/documentation/2.1.0/ScalaForms
If you don't need them in an object you could use a tuple
val userForm = Form(
tuple(
"fullName" -> text,
"username" -> text,
"password" -> text,
"confirm" -> text
)
)
The tuple in your case you have the following type: (String, String, String, String) which you could use like this: val (fullName, username, password, confirm) = refToTuple
Late coming to this, but I just released a utility to help with this! Using your classes, your code would look like this:
case class User(id: Int, username: String, password: String, fullname: String, lastLogin : Date, updatedOn: Date, updatedBy: Int, createdOn: Date, createdBy: Int)
object User { implicit val mapping = CaseClassMapping.mapping[User] }
val userForm = Form(implicitly[Mapping[User]])
You can find the source and instructions for including it in your project on github: https://github.com/Iterable/iterable-play-utils

Play framework Leon Salat Model Form Mapping

I'm creating a scala application using Play framework and mongoDB. I manage to have the connections up using Leon Play-Salat. I have a model
case class Person(
id: ObjectId = new ObjectId,
fname: String,
mname: String,
lname: String
)
In my controller I need to map it to a form
val personForm: Form[Person] = Form(
// Defines a mapping that will handle Contact values
mapping(
"id" -> of[ObjectId],
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)(Person.apply)(Person.unapply))
How do I map the ObjectID to the form ? I'm getting error Object not found for the ObjectId.
Manage to get it working
val personForm: Form[Person] = Form(
// Defines a mapping that will handle Contact values
mapping(
"id" -> ignored(new ObjectId),
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)(Person.apply)(Person.unapply))
I'm trying to do a CRUD function thus need the ID.
Found using own constructor and deconstructor is better
val personForm: Form[Person] = Form(
mapping(
"fname" -> nonEmptyText,
"mname" -> text,
"lname" -> nonEmptyText
)((fname, mname, lname) => Person(new ObjectId, fname, mname, lname))
((person: Person) => Some((person.fname, person.mname, person.lname))) )