The grails reference defines a constraint as "Allows the definition of declarative validation constraints". Grails comes built in with numerous constraints that fit most day to day needs: url, email, credit card, and many more! To define a set of constraints you only need to define a constraints block.
For example, imagine you need a "User" domain object, requiring a "name", and "email" properties. Our business rules dictate that the name property must not be blank, be between 3 and 30 characters, and the email address provided must be a valid email address. This class (and the required validation) would look like the following:
class User {
static constraints = {
name(size:3..30, blank: false)
email(email: true)
}
String name
String email
}
If these constraints are not enough you are provided with the ability to utilize a closure and write anything you need. Here is an example of a custom validator:
class User {
String login
static constraints = {
login(validator: {
if (it.contains('admin')) return ['invalid.login']
})
}
}
If you wish to define a constraint that can be used across classes you can implement the Constraint interface. I chose to extend AbstractConstraint which takes care of some of the underlying boilerplate code that would need to be written. Here is a fictional example which defines an isFred constrains which is applied to String values, and validates that the provided string does not contain "fred".
class IsFredConstraint extends AbstractConstraint {
private boolean fred;
public boolean supports(Class type) {
return type != null && String.class.isAssignableFrom(type);
}
public void setParameter(Object constraintParameter) {
if (!(constraintParameter instanceof Boolean))
throw new IllegalArgumentException("Parameter for constraint [isFred] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean value");
this.fred = ((Boolean) constraintParameter).booleanValue();
super.setParameter(constraintParameter);
}
public String getName() {
return "isFred";
}
protected void processValidate(Object target, Object propertyValue, Errors errors) {
if (fred) {
String value = propertyValue.toString();
def args = (Object[]) [constraintPropertyName, constraintOwningClass, propertyValue]
if (value.contains("fred")) {
super.rejectValue(target, errors, "invalid.fred", "not.isFred", args);
}
}
}
}
Any file ending in "Constraint" will auto-magically be added as a constraint so name the above file "FredConstraint.groovy" and place it in the /grails-app/utils directory. It can then be used like so:
static constraints = {
login(isFred:true)
}
This is my first foray into grail validation, so if there is a better approach please let me know in the comments!
You might also want to check out this plugin, which came out a few weeks ago.
ReplyDeletehttp://grails.org/plugin/constraints
It makes defining constraints even easier.
We had some problems with constraints and subclasses. It is a know bug: http://jira.codehaus.org/browse/GRAILS-1623
ReplyDelete