5

When using command objects like:

class UserCommand {

   String name

   static constraints = {
      name blank: false, unique: true, minSize: 3
   }
}

you can use them to validate objects without making them persistent. In my case I would validate for a persistent class User.

In controller:

def save(UserCommand cmd) {
  if(!cmd.validate()) {
      render view: "create", model: [user: cmd]
  return
  }
  def user = new User()
  user.name = cmd.name
  user.save()

  redirect uri: '/'

} 

in messages.properties:

user.username.minSize.error=Please enter at least three characters.
userCommand.username.minSize.error=Please enter at least three characters.

When using custom validation messages you have to write the message codes for each error twice. One for the User class and another for the UserCommand class.

Is there a way how I can have only one message code for each error?

Michael
  • 32,527
  • 49
  • 210
  • 370
  • This is really an intuitive question which made me think if I can achieve what you are expecting using the property `importFrom User` in constraints for `UserCommand`.But no it cannot achieve the goal. Finally,I think you have to have both the messages available. Refer [Validation and Internationalization](http://grails.org/doc/2.2.x/guide/single.html#validation) where it particularly says about Class name.Unless there is another way,I feel you have to stick to above in order to apply validation in Command Object and in Domain Object.Inheritance may be an option where I am skeptical. – dmahapatro Jun 11 '13 at 02:57

2 Answers2

5

I might be wrong here but if you're using just the stock Grails constraints, the only way to share a validation message is to simply rely on the default.x.x.message key/values in messages.properties. Otherwise messages are looked up via the following key form:

className.propertyName.errorcode...=

You can however use a custom validator and override what message key gets returned for the validation error.

class User {
  ...

  static constraints = {
    ...
    name blank: false, unique: true, validator: { value, user ->
      if(!value || value.length() < 3)
        return 'what.ever.key.in.messages.properties'
    }
  }
}

Then you can keep it all DRY by sharing constraints between classes via a global constraint or as @dmahapatro mentioned, with the use of an importFrom in your UserCommand like so,

class UserCommand {
 ...
 static constraints = {
   importFrom User
   ...
  }
}

If you have more complicated validation, you can create your own constraints classes. Here are some resources:

http://www.zorched.net/2008/01/25/build-a-custom-validator-in-grails-with-a-plugin/ http://blog.swwomm.com/2011/02/custom-grails-constraints.html

ikumen
  • 11,275
  • 4
  • 41
  • 41
  • Thought of the `default` message as well, but did not want to pollute the default behavior. It may affect other areas. As far as the custom message goes, I agree it is flexible and can be made DRY but again it has to be done for all those constraints that OP is referring to. like blank, unique, etc. Right? – dmahapatro Jun 11 '13 at 12:42
2
  1. using unique constraint in CommandObject makes no sense, because uniqueness of what would it check?
  2. you can validate domain objects without persisting them exactly the same way as command objects - using validate() method
  3. you can put a User object in command object, set constraints only for the domain class, and then validate User object being a part of command object

    class User { 
        String name
        static constraints = {
            name blank: false, unique: true, minSize: 3
        }
    }
    
    class UserCommand {
        User user 
        static constraints = {
            user validator: { it.validate() }
        }
    }
    
    user.username.minSize.error=Please enter at least three characters.
    
  • How are you going to bind `name` from this URL to Command Object?`http://localhost:8080/myapp/user/save?name=Fo`. OR How are you going to bind `name` to command object if submitted from a form? – dmahapatro Jun 11 '13 at 02:32
  • both cases, param/input name should be `user.name` so `http://localhost:8080/myapp/user/save?user.name=Fo` – Kamil Mikolajczyk Jun 11 '13 at 09:44
  • What does user.name mean to an end client? – dmahapatro Jun 11 '13 at 10:18
  • user is a name of User object reference inside the UserCommand class, and name is a field name in User class – Kamil Mikolajczyk Jun 11 '13 at 10:30
  • Yes exactly. Why should a consumer of this service should know about all those stuff done in the server side. All he/she should know that I have to send a name in the URL. Makes sense? :) – dmahapatro Jun 11 '13 at 10:34
  • this is only an input name, so you can name it xyz.abc, as long as it matches your field names it's fine - this is a convention you should follow if you want to achieve automatic binding – Kamil Mikolajczyk Jun 11 '13 at 10:39
  • @KamilMikolajczyk This is a good idea, but in your case I have to validate the whole User model which I do not want to do. – Michael Jun 14 '13 at 13:08