so, when coding grails apps it would be nice to validate some data in a domain objects field. There are many well known constraints in the grails distribution, like nullable, size or matches. But what happens if you need more validation? more in the sense of fine-grained, or some obscure piece of business logic? Upside of implementing your own little validator is that you can do everything (in a java sense!) and it will be enforced at the domain level. So you can’t persist any objects that fail the validation phase which preceeds the actual save() method. this is good: for instance from one of the current projects: i need to validate a piece of user input (well, its a 4 number string, actually), so i have the following constraints on a string field num:
blank: false
nullable: false
size: 4..4
matches: (some regexp checking its numerical here!)
statID: true
AHA! the last one is not prepackaged with grails but is used to represent and encapsulate the additional business logik required to validate the field against a remote application (vie WS call). My, my, my… what fun stuff can be done with this? And How to do it?
heres how to do it:
- think about the problem and how your going to validate it in a true/false fashion
- make a simple mock of what your trying to do (like call the Webservice from a grails service)
- make your implementation robust against network outages (ok, this is only cause my example uses a WS) and reuse service in a controller
- create a utility class (a POGO in the util folder of the grails app) and name it following the grails conventions: SomethingConstraint letting it extend AbstractConstraint
- implement the methods of the abstract baseclass, possibly reusing that service?!, this is the most work!
- hook it into the application by registering the constraint with grails
//register a custom constraint to validate againt the other System org.codehaus.groovy.grails.validation.ConstrainedProperty.registerNewConstraint( SomethingConstraint.SOME_CONSTRAINT, SomethingConstraint.class) - test it!
and heres the utility class:
import org.codehaus.groovy.grails.validation.AbstractConstraint
import org.springframework.validation.Errors
class StatIdConstraint extends AbstractConstraint{
ErhDBService erhDBService = new ErhDBService();
def NOT_A_STATID_MESSAGE_CODE = "keine StatID!"
public static final String STATID_CONSTRAINT = "StatID";
String getName() {
return this.STATID_CONSTRAINT
}
boolean supports(Class type) {
return type != null && String.class.isAssignableFrom(type);
}
protected void processValidate(Object target, Object propertyValue, Errors errors) {
if (!validateStatId(target, propertyValue)) {
def args = (Object[]) [constraintPropertyName, constraintOwningClass,
propertyValue]
super.rejectValue(target, errors, NOT_A_STATID_MESSAGE_CODE,
"not." + STATID_CONSTRAINT, args);
}
}
private boolean validateStatId(Object target, Object propertyValue) {
boolean valid = false;
String temp = propertyValue.toString()
valid = erhDBService.validateStatNr(temp)
return valid
}
}
the only downside to this approach i have been experiencing is that there is now a webservice call being made every time the object is persisted. this could turn out to be rather expensive, if i had millions of objects being saved… but i think for this app i will leave as-is. sure you can do all sorts of caching and optimization, but i think i will leave this to be done when the problem occurs…
last but not least i want to thank and link to http://www.zorched.net/2008/01/26/custom-validators-in-grails-in-a-single-app/, cause i dont think i could have done it in so little time without that excellent post.