It can be very easy to add validations to the play framework. Just adding annotations to the objects will usually do the trick, but these are for the values of one field only, like @Required or @Email. If you want to check if some combined properties are valid, then you can use the @CheckWith annotation. With this annotation you have to define a class that will be used for the validation and optionally a message if this fails. For instance, a check on multiple fields for uniqueness in the database can be handled this way. The checkwith class must extend Check. An example will make it more clear
@Entity public class User extends Model { @CheckWith(value = UniqueUserCheck.class, message = "error.not.unique") @Required @NotNull public String name; @Required @Email public String email; @NoBinding public boolean admin; private class UniqueUserCheck extends UniqueObjectCheck<User> { @Override protected List loadExistingUniqueObjects(final User user) { return User.find("byNameAndEmail", user.name, user.email).fetch(2); } } }
Some explanations, @Required tells us that the field is required. If not filled an error will be added to the validations. @CheckWith will check this object using the defined class. I have added the class that checks if there already exists a user with this name and email in the user class itself. This class extends the UniqueObjectCheck which consists of :
public abstract class UniqueObjectCheck<T extends Model> extends Check { @Override @SuppressWarnings("unchecked") public boolean isSatisfied(final Object validatedObject, final Object value) { boolean checkOk = true; Class modelType = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; if (validatedObject != null && modelType.isAssignableFrom(validatedObject.getClass())) { T item = (T) validatedObject; List existingObjects = loadExistingUniqueObjects(item); checkOk = existingObjects.size() == 0; if (existingObjects.size() == 1) { if (item.id != null) { T existingClass = existingObjects.get(0); if (existingClass.getId().equals(item.id)) { checkOk = true; } } } } return checkOk; } protected abstract List loadExistingUniqueObjects(final T item); }
So this class loads the existing objects as defined by the extending class. If it finds one or more items then it checks the item for an id. If the id is not the same as the given id then an object with these unique fields already exists and thus this object should not be saved.
Another important annotation is the @NoBinding annotation. This tells the playframework to not bind any incoming http parameters to the admin field. This prevents some smart user from adding the parameter admin=true to the request. The @NoBinding annotation can also take values if needed to tell when the binding is allowed to happen or not. If a value is set on the @NoBinding, then that value should also be used in the notation of the method to indicate that in this case there should be no binding, e.g.
@NoBinding("editUser") public boolean admin
and in the controller
public void saveUser(@As('editUser') User user)
will prevent the admin field of being set. If you have a special admin method which does allow it, then the As can be skipped and the admin field will be updated, e.g.
@Secure.isAdmin public void saveUser(User user)
Now that all the plumbing for the validations are in place, its time to validate your object. This can be doen by:
public void saveUser(@As('editUser') @Valid User user)
That’s it. You almost didnt notice it, but just adding the @Valid for the object to be validated will call all the validation rules. It’s that simple with the playframework.
Now that you have validated its time to check the validations but more on that subject in a next blog
great article, looking fordward for more articles on validations…
This does not work for me, I get the following error:
The file /app/validation/UniqueObjectCheck.java could not be compiled. Error raised is : T cannot be resolved to a type
This means that you didn’t define the type of the UniqueObjectCheck, so you should have something like the following:
and you probably did it like this:
Which was a typo in my post I see now because wordpress saw the ‘<User>’ as an invalid html tag, so it wasnt shown correctly
Thx for the reply, I will fix the post as well
Yes, this was the error. Works perfectly now.