Search code examples
validationgrailsgrails-orm

grails - validation of inherited domain class attributes


I have a fairly straight-forward model of users with profiles and experts, who are special users:

class User { static hasOne = [profile: Profile] }

class Profile {
    String country
    static constraints = { country nullable: true, blank: true }
}

class Expert extends User {
    static constraints = {
        profile validator: { val, obj ->
            if (!val.country) {
                val.errors.rejectValue 'country', 'expert.profile.country.nullable'
            }
        }
    }
}

When I create an expert, set their profile properties and then save the expert, this works as it should. However, if a user wants to save their profile, I am stuck with how to properly validate the profile properties depending on whether they are an expert or not.

This is what I have so far:

    Expert expert = Expert.get(profile.user.id)
    if (expert) {
        expert.properties = params
        expert.save()
    } else {
        profile.user.properties = params
        profile.user.save()
    }

This code does the proper validation and sets the correct error message. However, there are (at least) three issues:

  1. The service doing the profile updating shouldn't really have to know anything about the different types of users.
  2. While the errors are set on the profile, the profile is still saved to the DB.
  3. The error code is not turned into a message until a second attempt is made to update the profile.

What is the proper way to validate an inherited domain class attribute? Alternatively, is there a better model that I could use to accomplish the goal of different types of users with role-specific validation requirements?

EDIT: It turned out that only the first of the three issues was real. The other two were caused by a tag lib saving the user object while trying to query the DB. The accepted solution (validation in Profile based on a user flag) addresses that.


Solution

  • I think you have to set that the profile belongs to an user, and could be nullable. After that you can create the validator inside the profile class. Maybe for you the expert type needs a new class, but i am not sure if that is compulsory.

    Maybe I could implement a method inside the User class to know if an entity is an expert based on its properties, or a property itself

    class User { 
        static hasOne = [profile: Profile] 
        boolean expert
    }
    
    class Profile {
        static belongsTo = [user: User]
        String country
        static constraints = { 
            country validator: { val, obj ->
                if (obj.user?.expert && !val.country) {
                    val.errors.rejectValue 'country', 'expert.profile.country.nullable'
                }
            } 
            user nullable: true
        }
    }