CakePHP 3多个isUnique允许NULL重复

时间:2016-03-16 09:35:46

标签: php validation cakephp cakephp-3.0

我的BlockedTable.php中有以下规则

public function buildRules(RulesChecker $rules)
{
    $rules->add($rules->isUnique(['date', 'time']),
        ['message' => 'unique error']);

    return $rules;
}

到目前为止,这已经运行良好 - 如果我尝试使用已有的日期和时间保存新记录,则会阻止我保存。

但是,如果我的时间是NULL,例如以下条目;

╔════╦══════════════╦═══════╗
║ ID ║  Date        ║ Time  ║
╠════╬══════════════╬═══════╣
║  1 ║ 22/08/1985   ║ NULL  ║
╚════╩══════════════╩═══════╝

规则仍然允许我保存具有相同数据的新条目。因此,如果我尝试保存$date = 22/08/1985$time = NULL,它会保存正常,并且我的数据库中存在重复记录。由于上述规则,我原本预计会失败吗?

为什么会这样?如何防止NULL值重复输入?

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

使用普通比较运算符与NULL进行比较,即column = NULL(唯一规则所做的),应始终为NULL(AFAIK至少在MySQL和Postgres中就是这种情况)因此,没有找到任何内容,因此只要涉及NULL值,就会通过唯一检查。

如果要阻止此行为,则必须使用自定义规则,因为内置规则根本不支持它。我认为这是一个值得改进的东西,所以你可能想打开一张票over at GitHub

以下是覆盖IsUnique规则类的基本示例,它基本上只是将IS运算符添加到条件键,以便NULL检查最终为column IS NULL

public function __invoke(EntityInterface $entity, array $options)
{
    if (!$entity->extract($this->_fields, true)) {
        return true;
    }

    $alias = $options['repository']->alias();
    $conditions = $this->_alias($alias, $entity->extract($this->_fields));
    if ($entity->isNew() === false) {
        $keys = (array)$options['repository']->primaryKey();
        $keys = $this->_alias($alias, $entity->extract($keys));
        if (array_filter($keys, 'strlen')) {
            $conditions['NOT'] = $keys;
        }
    }

    // handle null values
    foreach ($conditions as $key => $value) {
        if ($value === null) {
            $conditions[$key . ' IS'] = $value;
            unset($conditions[$key]);
        }
    }

    return !$options['repository']->exists($conditions);
}

理论上你也可以用覆盖的IsUnique::_alias()方法来实现它,这可以在不必重新实现原始规则类的代码的情况下工作,但它确实不是正确的地方。

<强> https://github.com/cakephp/cakephp/blob/3.2.5/src/ORM/Rule/IsUnique.php

另见

答案 1 :(得分:1)

我知道这是一篇很老的帖子,但由于我必须经常搜索才能解决这个问题,我想我应该在这里发布我是如何做到的。

在cakePHP 3.6中,您可以通过在类isUnique中将属性allowMultipleNulls更改为false来解决此问题。

问候。