简体   繁体   English

如何为Grails域类中的两个字段创建和XOR验证?

[英]How do I create and XOR validation for two fields in a Grails domain class?

I have an issue where my domain class has two potential mutually exclusive external keys, either a serial number or a legacy lookup value. 我有一个问题,我的域类有两个潜在的互斥外部密钥,序列号或遗留查找值。

Since I'm not sure which one I'll have for any given entry, I've made them both nullable and added custom validation to try to ensure I have one and only one value. 由于我不确定任何给定条目我将拥有哪一个,我已经使它们都可以为空并添加自定义验证以确保我只有一个值。

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

I created the default CRUD screens and tried to create an entry for this domain class 我创建了默认的CRUD屏幕,并尝试为此域类创建一个条目

information: Blah Blah
serialNumber: 
legacyLookup: BLAHINDEX123

This dies in the validator with the following message: 这在验证器中死亡,并带有以下消息:

No such property: serialNumber for class: myproject.Sample

What am I missing? 我错过了什么?

Having each property in there multiple times is not necessary; 在那里多次拥有每个财产是没有必要的; in fact you only need one of them actually constrained. 实际上你只需要其中一个实际受约束。 Also you can't just reference properties directly by their name. 此外,您不能直接通过名称引用属性。 There are objects that are passed to the constraint closure that are used to get at the values (see the docs ). 传递给约束闭包的对象用于获取值(请参阅文档 )。 Probably the simplest way I've found to do this is as follows: 可能我发现这样做最简单的方法如下:

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

And then have an entry in the messages.properties file like this: 然后在messages.properties文件中有一个条目,如下所示:

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

Or you could have separate messages for each condition - both are entered, or both are blank like this: 或者你可以为每个条件分别发送消息 - 两者都输入,或者两者都是空白的,如下所示:

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

And then have two messages in message.properties: 然后在message.properties中有两条消息:

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

You don't need to return anything from the constraint if there is no error... 如果没有错误,您不需要从约束中返回任何内容......

If you want to ensure you have "one and only one value" another option would be to make a generic field called serialNumberLegacyLookup that would represent both the serialNumber and legacyLookup fields. 如果你想确保你有“一个且只有一个值”,另一个选择是创建一个名为serialNumberLegacyLookup的通用字段,它代表serialNumberlegacyLookup字段。 Then you could add a boolean field to your domain class called legacyLookup which would default to false . 然后你可以在域类中添加一个名为legacyLookup的布尔字段,默认为false You could then run the value through the validator and parse it to see if it was a "serial number" or a "legacy lookup" value. 然后,您可以通过验证程序运行该值并对其进行解析,以查看它是“序列号”还是“遗留查找”值。 If the value turned out to be a "legacy lookup" value then you would set the legacyLookup boolean to true . 如果该值被证明是“遗留查找”值,那么您将legacyLookup boolean设置为true I think this approach would be less confusing from a UI perspective (only one field for the user to fill in instead of two conditional fields). 我认为从UI的角度来看,这种方法会更少混淆(用户只需填写一个字段而不是两个条件字段)。

I was faced with this same scenario and the solution I found was to create a getter method and add a constraint to it. 我遇到了同样的情况,我找到的解决方案是创建一个getter方法并为其添加一个约束。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM