2012-07-14 14 views
5

Tengo un problema donde mi clase de dominio tiene dos posibles claves externas mutuamente excluyentes, ya sea un número de serie o un valor de búsqueda heredado.¿Cómo creo y la validación XOR para dos campos en una clase de dominio Grails?

Dado que no estoy seguro de cuál voy a tener para una entrada determinada, las he convertido en anulables y he añadido una validación personalizada para intentar asegurar que tengo un solo valor.

package myproject 

class Sample { 

    String information 
    String legacyLookup 
    String serialNumber 

    static constraints = { 
     information(nullable: true) 
     legacyLookup(nullable: true) 
     serialNumber(nullable: true) 

     legacyLookup validator: { 
      return ((serialNumber != null && legacyLookup == null) || (serialNumber == null && legacyLookup != null)) 
     } 

     serialNumber validator: { 
      return ((serialNumber != null && legacyLookup == null) || (serialNumber == null && legacyLookup != null)) 
     } 
    } 
} 

creé las pantallas CRUD defecto y trataron de crear una entrada para esta clase de dominio

information: Blah Blah 
serialNumber: 
legacyLookup: BLAHINDEX123 

Esta muere en el validador con el siguiente mensaje:

No such property: serialNumber for class: myproject.Sample 

¿Qué soy yo ¿desaparecido?

Respuesta

9

Tener cada propiedad allí varias veces no es necesario; de hecho, solo necesitas uno de ellos realmente limitado. Además, no puede simplemente hacer referencia a las propiedades directamente por su nombre. Hay objetos que se pasan al cierre de restricciones que se utilizan para obtener los valores (consulte docs). Probablemente la forma más sencilla que he encontrado para hacer esto es el siguiente:

class Sample { 
    String information 
    String legacyLookup 
    String serialNumber 

    static constraints = { 
     information(nullable: true) 
     legacyLookup(validator: {val, obj-> 
      if((!val && !obj.serialNumber) || (val && obj.serialNumber)) { 
       return 'must.be.one' 
      } 
     }) 
    } 
} 

Y luego tener una entrada en los messages.properties presentar la siguiente manera:

must.be.one=Please enter either a serial number or a legacy id - not both 

o podría tener mensajes separados para cada condición - ambos son introducidos, o ambos están en blanco como esto:

legacyLookup(validator: {val, obj-> 
    if(!val && !obj.serialNumber) { 
     return 'must.be.one' 
    } 
    if(val && obj.serialNumber) { 
     return 'only.one' 
    } 
}) 

Y entonces dos mensajes en message.properties:

only.one=Don't fill out both 
must.be.one=Fill out at least one... 

Usted no necesita devolver algo de la restricción si no hay errores ...

+0

Esto hizo exactamente lo que necesitaba. Estaba pensando que necesitaba una validación explícita en ambos campos, pero uno maneja el otro. ¡Gracias! – GeoGriffin

0

Si desea asegurarse de tener "un solo valor", otra opción sería crear un campo genérico llamado serialNumberLegacyLookup que represente los campos serialNumber y legacyLookup. Luego, podría agregar un campo booleano a su clase de dominio llamada legacyLookup, que sería el predeterminado en false. Luego puede ejecutar el valor a través del validador y analizarlo para ver si era un valor de "número de serie" o de "búsqueda heredada". Si el valor resultó ser un valor de "búsqueda heredada", entonces establecería el booleano legacyLookup en true. Creo que este enfoque sería menos confuso desde una perspectiva de UI (solo un campo para que el usuario complete en lugar de dos campos condicionales).

+0

Como el código con el que estoy trabajando interactúa con sistemas heredados externos, no creo que esta solución sea adecuada. Los datos que recibo tienen un campo u otro, pero se modelan como campos únicos. Puede haber algunas reglas de negocio que me son desconocidas en este momento que pueden romper mi comprensión del modelo. Para aislarme de esa posibilidad, los mantendré separados. – GeoGriffin

0

me encontré con este mismo escenario y la solución que encontré fue la creación de un método getter y agregar una restricción lo.

package myproject 

class Sample { 

    String information 
    String legacyLookup 
    String serialNumber 

    def getTarget(){ 
     if (legacyLookup && !serialNumber) { 
      return legacyLookup 
     } else if (!legacyLookup && serialNumber) { 
      return serialNumber 
     } else { 
      return null 
     } 
    } 

    static constraints = { 
     information(nullable: true) 
     legacyLookup(nullable: true) 
     serialNumber(nullable: true) 
     target(nullable: false) 
    } 
} 
Cuestiones relacionadas