3

I have a restriction so there could be no more than ConfigurationHolder.config.support.reminder.web.person.max object stored. I didn't find how to add a validator which doesn't relate on particular property. So for now I implemented it in this way. Do you guys have any ideas how to make it better?

package support.reminder.web

import org.codehaus.groovy.grails.commons.ConfigurationHolder;

class Person {

    String firstName
    String lastName
    String email
    Date lastDutyDate

    static constraints = {
        firstName(blank: false)
        lastName(blank: false)
        email(blank: false, email: true)
        lastDutyDate(nullable: true)
        id validator: {val ->
        if (val)
            Person.count() <= ConfigurationHolder.config.support.reminder.web.person.max
        else
            Person.count() < ConfigurationHolder.config.support.reminder.web.person.max
    }
    }

    String toString() {
        "[$firstName $lastName, $email, $lastDutyDate]"
    }
}
Aleksey
  • 1,309
  • 2
  • 10
  • 12

3 Answers3

5

You can use the Grails Custom Constraints Plugin to manage your validation implementation. Then you can call your own constraint just like the predefined Grails constraints:

package support.reminder.web

import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH

class Person {

    String firstName
    String lastName
    String email
    Date lastDutyDate

    static constraints = {
        firstName(blank: false)
        lastName(blank: false)
        email(blank: false, email: true)
        lastDutyDate(nullable: true)
        id(maxRows: CH.config.support.reminder.web.person.max)
    }

}

Alternatively, if you don't want to rely on 3rd Party Plugins you can implement the logic of your custom validator within a Service method but call it from the custom validator in the Domain:

package support.reminder.web

import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH

class Person {

    def validationService
    String firstName
    String lastName
    String email
    Date lastDutyDate

    static constraints = {
        firstName(blank: false)
        lastName(blank: false)
        email(blank: false, email: true)
        lastDutyDate(nullable: true)
        id (validator: {val ->
           validationService.validateMaxRows(val, CH.config.support.reminder.web.person.max)
        }
    }

}
david
  • 2,529
  • 1
  • 34
  • 50
  • I think that would make sense if I used this validator for others domain classes. But since I need it only in 1 place so I guess my solution is good for now – Aleksey Feb 02 '11 at 14:04
  • You are right. My understanding was you where searching for general possible improvements to avoid to much code in your domain. – david Feb 04 '11 at 18:35
0

I don't have a better idea, but I do suggest that maybe you need to check for < not <=. I think that when validating your object, it has not yet been stored in the DB, so it will not be included in Person.count(). I reckon that <= would cause it to pass the validation then be saved, then you would be violating the rule.

Fletch
  • 4,829
  • 2
  • 41
  • 55
  • I use <= intentionally. What if we have the restriction up to 7 persons max. And we have 7 persons stored. And we're updating any of persons. Then validation will fail because 7 < 7 will return false. I have additional validation for UI and it won't let you create more person than it's possible. But I want to have the restriction in the domain layer as well. – Aleksey Jan 19 '11 at 09:03
  • Oh, Fletch, you are absolutely right about the problem with < and <=. I found a better solution for that. I've updated my code – Aleksey Jan 19 '11 at 15:17
0

I recommend you using a service function, like personService.addPerson(). Then check the constraint before you save a new object. It will benefit if you get more complicated constraint, such as when it related many domain objects, for example.

The use of a validator for restricting the number of object is actually not very good if concerning about the meaning of validator: the object is valid, only the number of objects is too large.

In short: the logical code goes to the service.

Hoàng Long
  • 10,746
  • 20
  • 75
  • 124
  • I can't agree with it. I'm convinced that the business logic should be in a domain layer, while the application logic in service one. Maybe I'm wrong with this particular example, but I think this restriction should be in the domain layer. – Aleksey Jan 20 '11 at 10:54
  • @Aleksey: that's my opinion: semantically, it's a little strange that an object is called invalid just because there's too many of object already. It's kind of vague that the restriction here belongs to business or domain, so you can do it that way – Hoàng Long Jan 21 '11 at 02:23