我的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
值重复输入?
提前感谢您的帮助。
答案 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来解决此问题。
问候。