Grails: revalidation in Command Objects

Grails: revalidation in Command Objects

Command objects в Grails — очень полезные штуки. Это как бы domain objects, но не имеющие связи с базой данных. Зато подвергающиеся валидации. Это классно, если в приложении есть формы, которые должны проверять входные данные, но не хранить их в базе (формы логинов, отправки сообщений, да вообще просто формы).

Но есть у них одно неприятное свойство. Они так устроены, что цепляются к методу контроллера и принимают данные автоматически — то есть, в момент инициализации контроллера данные из запроса автоматически биндятся в комманд обжект и проходят первую стадию валидации. Соответственно, если в CO определены свойства таких типов, которых не может быть в параметрах запроса — например, Date особенно если дата в форме собирается из нескольких полей (типа даты, часов и минут). То есть, по хорошему мы должны бы иметь такой код:

 

class SomeController {
  def index = {MyCommand cmd ->
    def date = ... // код, который собирает дату из разных полей
    cmd.date = date
    if(!cmd.hasErrors()){
     
    }
  }
}

 

Но такая тема не пройдет, потому что во время выполнения кода метода объект cmd уже содержит в себе ошибку биндинга и очень даже hasErrors. А вот способа повторной валидации данных Grails не предлагает.

Что же можно сделать? Можно сделать плагин, который вполне может решить эту проблему. Просто создадим соответствующий метод и назначим его всем Command Objects. Код, который занимается биндингом копипастим с небольшими изменениями из ControllerGrailsPlugin:

 

import org.codehaus.groovy.grails.web.metaclass.*
import org.springframework.web.context.request.RequestContextHolder as RCH

public class SomeGrailsPlugin{
    def version = "0.1"
   
    def watchedResources = [
    "file:./grails-app/controllers/**/*Controller.groovy",
    "file:./plugins/*/grails-app/controllers/**/*Controller.groovy"
    ]
   
    def doWithDynamicMethods = { applicationContext ->
        createRevalidateMethod(application, applicationContext)
    }
   
    def onChange = {event ->
        println 'change catched in some!'
        createRevalidateMethod(application, event.ctx)
    }
   
    def createRevalidateMethod(application, applicationContext){
        def bind = new BindDynamicMethod()
        application.controllerClasses.each {controller ->
            def commandObjectClasses = controller.commandObjectClasses
            commandObjectClasses.each {commandObjectClass ->
                commandObjectClass.metaClass.revalidate = {
                        def commandObject = delegate
                        def params = RCH.currentRequestAttributes().params
                        bind.invoke(commandObject, "bindData", [commandObject, params] as Object[])
                        def errors = commandObject.errors ?: new BindException(commandObject, paramType.name)
                        def constrainedProperties = commandObject.constraints?.values()
                        constrainedProperties.each {constrainedProperty ->
                            constrainedProperty.messageSource = applicationContext.getBean("messageSource")
                            constrainedProperty.validate(commandObject, commandObject.getProperty(constrainedProperty.getPropertyName()), errors);
                        }
                        commandObject.errors = errors
                }
            }
        }
    }
}

 

И изменяем наш контроллер:

 

class SomeController {
  def index = {MyCommand cmd ->
    def date = ... // код, который собирает дату из разных полей
    cmd.date = date
    cmd.revalidate()
    if(!cmd.hasErrors()){
     
    }
  }
}

 

Ну вот, теперь стало значительно легче. Энджой!

 

Комментарии (0)

mem: 1158 total: 28 module: 18 xsl: 6