Grails
  1. Grails
  2. GRAILS-6584

Allow reuse of a specified set of domain class constraints in command class constraints

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.3.3
    • Fix Version/s: 2.0 final
    • Component/s: None
    • Labels:
      None
    • Environment:
      Grails 1.3.3
      sun jdk 1.6.0_21 b07
      windows 7 64 bit

      Description

      I think it will be very useful to have a possibility for reusing specified set of constraints from domain class in command class. Besides being in line with DRY principle, it will prevent errors when one forget to update changed constraint in both classes.

      To better explain what I mean, here is some sample code:

      import org.codehaus.groovy.grails.validation.ConstrainedProperty;
      import org.codehaus.groovy.grails.validation.Constraint;
      
      class MyGrailsUtils {
        static void copyConstraintsFromDomainToCommand(
          def p_domainClass, def p_commandClass, String p_constrainedPropertyName, String p_constraintName)
        {
          Map domainClassConstraints = p_domainClass.getConstraints()
          ConstrainedProperty domainClassConstrainedProperty = domainClassConstraints[p_constrainedPropertyName]
          Constraint domainClassPropertyConstraint = domainClassConstrainedProperty.getAppliedConstraint(p_constraintName)
      
          String domainClassPropertyConstraintName = domainClassPropertyConstraint.getName()
          Object domainClassPropertyConstraintParameter =  domainClassPropertyConstraint.constraintParameter
      
          def commandClassConstraints = p_commandClass.getConstraints()
          Map commandClassConstrainedProperties = commandClassConstraints.getConstrainedProperties()
          ConstrainedProperty commandClassConstrainedProperty = commandClassConstrainedProperties[p_constrainedPropertyName]
          commandClassConstrainedProperty.applyConstraint(p_constraintName, domainClassPropertyConstraintParameter)
        }
      }
      
      class User {
        String userId
        ...
        static constraints = {
          userId(blank:false, size: 3..20, unique: true)
          ...
        }
        ...
      }
      
      class UserRegistrationCommand {
        String userId
        ...
        static constraints = {
          userId(blank:false)
          ...
          // Copies size constraint of userId property from User domain class.
          MyGrailsUtils.copyConstraintsFromDomainToCommand(User, UserRegistrationCommand, "userId", "size")
        }
      }
      

      Code is not tested, and I'm sure it could be written much better since I'm groovy/grails beginner, and code uses some non public method and properties. However, I hope it can illustrate the idea. Of course, it would be the best to add this feature in constraints DSL somehow.

      1. 0001-GRAILS-6584-Fixed-Allow-reuse-of-constrains-in-comma.patch
        31 kB
        Damir Murat

        Activity

        Hide
        Stéphane Maldini added a comment -

        Add to doc

        Show
        Stéphane Maldini added a comment - Add to doc
        Hide
        Bobby Warner added a comment -

        The commit (8313225b486b99ff64d919c7ee3d6fc4169962c1) is included in 2.0 Final, so this issue should be closed. Thanks, Bobby

        Show
        Bobby Warner added a comment - The commit (8313225b486b99ff64d919c7ee3d6fc4169962c1) is included in 2.0 Final, so this issue should be closed. Thanks, Bobby
        Hide
        Graeme Rocher added a comment -

        Thanks Bobby

        Show
        Graeme Rocher added a comment - Thanks Bobby
        Hide
        Peter Ledbrook added a comment -

        What happens with the nullable constraint, since that has a different default for domain classes and command objects? If it's not specified in a domain class and a command object imports the constraint, does the command object constraint have nullable: false or nullable: true?

        Show
        Peter Ledbrook added a comment - What happens with the nullable constraint, since that has a different default for domain classes and command objects? If it's not specified in a domain class and a command object imports the constraint, does the command object constraint have nullable: false or nullable: true ?
        Hide
        Damir Murat added a comment -

        It was long time ago , but as I can remember, mechanism is based on applied constraints, meaning, whichever constraint is applied on domain object, it will be applied on a command, including nullable: false as default for domain objects. I created simple app to test this, by using both DefaultConstraintEvaluator on command class and validate() method on command instance, and results confirm this.

        If this is not desired, one can always modify specific constraint value in command class. For example, in domain class

          class Person {
            String firstName
            
            static constraints = {
              firstName(blank: false, maxSize: 50)
            }
          }
        

        for firstName property, we have applied constraints of nullable: false, blank: false and maxSize: 50. If command class is specified as

          class PersonCommand {
            String firstName
            
            static constraints = {
              importFrom Person
              
              firstName(nullable: true)
            }
          }
        

        this will result with constraints nullable: true, blank: false and maxSize: 50 for firstName property in command class, meaning only specified constraint will be modified, others, which are copied from domain object, are left untouched.

        I think that nullable constraint behavior is same for domain and command classes, with difference how grails applies it to properties of each kind of class. I mean, if no constraints are specified at all for domain class property, nullable: false will be automatically added on that property. For command class property, if no constraints are specified, nullable will not be added, but if any other constraint is specified, nullable: false will be added to command class property. I might be mistaken with this, but I believe things works that way...

        Show
        Damir Murat added a comment - It was long time ago , but as I can remember, mechanism is based on applied constraints, meaning, whichever constraint is applied on domain object, it will be applied on a command, including nullable: false as default for domain objects. I created simple app to test this, by using both DefaultConstraintEvaluator on command class and validate() method on command instance, and results confirm this. If this is not desired, one can always modify specific constraint value in command class. For example, in domain class class Person { String firstName static constraints = { firstName(blank: false, maxSize: 50) } } for firstName property, we have applied constraints of nullable: false , blank: false and maxSize: 50 . If command class is specified as class PersonCommand { String firstName static constraints = { importFrom Person firstName(nullable: true) } } this will result with constraints nullable: true , blank: false and maxSize: 50 for firstName property in command class, meaning only specified constraint will be modified, others, which are copied from domain object, are left untouched. I think that nullable constraint behavior is same for domain and command classes, with difference how grails applies it to properties of each kind of class. I mean, if no constraints are specified at all for domain class property, nullable: false will be automatically added on that property. For command class property, if no constraints are specified, nullable will not be added, but if any other constraint is specified, nullable: false will be added to command class property. I might be mistaken with this, but I believe things works that way...

          People

          • Assignee:
            Stéphane Maldini
            Reporter:
            Damir Murat
          • Votes:
            7 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Last Reviewed:

              Development