Grails domain validator with exception

In this article, I will explains how we can improve the validation flow provided by grails.

Usual pattern

When using default grails scaffolding a controller action, for example save, will look some thing like this :

def save = {
def fooInstance = new Foo(params)
if(!fooInstance.hasErrors() && fooInstance.save()) {
 flash.message = "Foo ${fooInstance.id} created"
 redirect(action:show,id:fooInstance.id)
}else {
 render(view:'create',model:[fooInstance:fooInstance])
 }
}

Runtime Exceptions in a service cause the transaction to be rolled back, which is the desired behavior when a validation error has occurred.

Service

class FooService {
boolean transactional = true
 
Foo saveFoo(Map params) {
def fooInstance = new Foo(params)
   if (!fooInstance.hasErrors() && fooInstance.save()) {
     return fooInstance
   } else {
     throw new ValidationException("Foo is not Valid", fooInstance)
   }
 }
}
Tip: If you are using grails 1.2 support for this is built into the framework You can now call save(failOnError:true) and and exception will be thrown. Or better yet you can set the configuration parameter grails.gorm.save.failOnError = true to enable in your entire application.

Controller

class FooController {
  def fooService
 
  def save = {
    try {
      def fooInstance = fooService.saveFoo(params)
      redirect(action: show, id: fooInstance.id)
    } catch (ValidationException e) {
      log.warn("Validation Failed on Foo ${e.message}" )
      render(view: 'create', model: [fooInstance: e.invalidObject])
    }
  }
}

Validation Exception

class ValidationException extends RuntimeException {
 Object invalidObject
 
ValidationException(String message, Object invalidObject){
super(message)
this.invalidObject = invalidObject
}
}

Source: http://keithsgrailstips.blogspot.com/2010/02/use-exceptions-to-control-validation.html