2014-03-03 20 views
0

任务是支持用户名被存储在数据库中,因为它们最初是在创建时键入的,并且使用'ilike'准则检查唯一性'eq'。编写一个必须在命令对象和域对象上工作的自定义grails唯一约束

在挖掘中,我看到了2个实现UniqueConstraint,其中一个由grails-datastore-gorm提供,另一个由grails-hibernate提供。我知道我需要在一个块中执行这个查找,因为我在会话对象上重置了FlushMode,所以hibernate在数据验证之前不会将更改保存到数据库。这是我写的自定义的验证:

@Override 
protected void processValidate(final Object target, final Object propertyValue, Errors errors) { 
    def reject = false 
    doWithManualSessionIfAppropriate { 
     def targetId = null 
     try { 
      targetId = InvokerHelper.invokeMethod(target, 'ident', null) 
     } catch (Exception e) { 
      throw new GrailsRuntimeException('Could not determine id of target') 
     } 

     def results = [] 

     results += constraintOwningClass."findAllBy${GrailsNameUtils.getClassName(constraintPropertyName, '')}Ilike"(propertyValue) 

     reject = results.any { 
      try { 
       def existingId = InvokerHelper.invokeMethod(it, 'ident', null) 
       targetId != existingId 
      } catch (Exception e) { 
       // the existing field is not in the db 
       false 
      } 
     } 
    } 

    if (reject) { 
     errors.rejectValue(constraintPropertyName, 'unique') 
    } 
} 

这让我们的集成测试都通过了,但是当我在调用importFrom来验证用户名控制器中使用它,invokeMethod未能找到命令对象的ID。例如:

RealUser.groovy:

class RealUser { 
    String username, passwordHash 
    static constraints = { 
     username iunique: true // where iunique is the name of my registered constraint 
    } 
} 

UserController.groovy:

@Validateable 
class UserCommandObject { 
    String username, password, passwordConfirmation 
    static constraints = { 
     importFrom RealUser 
    } 
} 

当我对RealUserunique: true,验证就好了UserCommandObject。当我将其更改为我的自定义验证程序iunique: true时,UserCommandObject抱怨说它不是域类。

我不可能看到unique: true如何用于命令对象,因为我看到的两个实现只能在域对象上工作(并且在没有域对象时调用GrailsRuntimeException)。

任何想法?我在想这个太过分了吗?

从另一个角度接近,没有任何令人信服的理由支持存储用户名作为输入的,只是呼吁输入验证lower()之前?

回答

1

会在自定义验证程序中findByPropertyNameIlike(val)这样做吗?

static constraints = { 
    propertyName blank: false, validator: { val, obj, errors -> 

     def results = this.findByPropertyNameIlike(val) 

     if(results) { 
      errors.rejectValue('propertyName', 'unique.propertyName') 
      return false 
     } 

     return true 
    } 
}