2012-07-14 18 views
5

Mam problem, w którym moja klasa domeny ma dwa potencjalne wzajemnie wykluczające się klucze zewnętrzne, albo numer seryjny, albo starszą wartość wyszukiwania.Jak utworzyć i zatwierdzić XOR dla dwóch pól w klasie domeny Grails?

Ponieważ nie jestem pewien, który z nich będę mieć dla danego wpisu, zrobiłem je zarówno zerowalne i dodane niestandardowe sprawdzanie poprawności, aby spróbować zapewnić, że mam jedną i tylko jedną wartość.

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)) 
     } 
    } 
} 

stworzyłem ekrany domyślne CRUD i próbował stworzyć wpis dla tej klasy domeny

information: Blah Blah 
serialNumber: 
legacyLookup: BLAHINDEX123 

Ten umiera w walidator z następującym komunikatem:

No such property: serialNumber for class: myproject.Sample 

co ja brakujący?

Odpowiedz

9

Posiadanie każdej własności tam wiele razy nie jest konieczne; w rzeczywistości potrzebujesz tylko jednego z nich w rzeczywistości ograniczonego. Ponadto nie można po prostu odwoływać się do właściwości bezpośrednio po nazwie. Istnieją obiekty, które są przekazywane do zamknięcia ograniczenia, które są używane do uzyskania wartości (zobacz docs). Prawdopodobnie najprostszym sposobem znalazłem to zrobić w następujący sposób:

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' 
      } 
     }) 
    } 
} 

A potem mieć wpis w messages.properties złożyć tak:

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

Albo można mieć oddzielne wiadomości dla każdy warunek - oba są wprowadzane, lub obie są puste tak:

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

a potem mieć dwa komunikaty w message.properties:

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

Nie trzeba powrócić niczego z przymusu, jeśli nie ma błędu ...

+0

To zrobiło dokładnie to, czego potrzebowałem. Myślałem, że potrzebuję zatwierdzenia jawnie na obu polach, ale jeden zajmuje się drugim. Dzięki! – GeoGriffin

0

Jeśli chcesz mieć "jedną i tylko jedną wartość", inną opcją byłoby utworzenie ogólnego pola o nazwie serialNumberLegacyLookup, które reprezentowałoby zarówno pola serialNumber, jak i legacyLookup. Następnie można dodać pole boolowskie do klasy domeny o nazwie legacyLookup, która domyślnie będzie miała wartość false. Następnie można uruchomić wartość za pośrednictwem walidatora i przeanalizować, czy jest to "numer seryjny" lub wartość "dziedzictwo starszego typu". Jeśli wartość okazała się wartością "starszego wyszukiwania", wówczas ustawiono wartość logiczną legacyLookup na true. Myślę, że to podejście byłoby mniej dezorientujące z punktu widzenia interfejsu użytkownika (tylko jedno pole do wypełnienia zamiast dwóch pól warunkowych).

+0

Ponieważ kod, nad którym pracuję, współdziała z zewnętrznymi systemami, nie sądzę, aby to rozwiązanie było odpowiednie. Dane, które przychodzą do mnie, mają jedno pole lub drugie, ale są modelowane jako unikalne pola. W tym momencie mogą istnieć reguły biznesowe, które mogą mi przeszkodzić w zrozumieniu modelu. Aby odizolować się od tej możliwości, zamierzam trzymać ich oddzielnie. – GeoGriffin

0

miałem do czynienia z tym samym scenariuszem, a roztwór znalazłem było stworzenie metody getter i dodać ograniczenie do tego.

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) 
    } 
}