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