如何根据多个实体本身以及数据库中的现有行验证多个实体



我有一个应用程序,需要在其中验证工作时间,以确保它们在CakePHP 3中不会重叠。所有记录都可以在一个表单中更改。数据看起来有点像:

打开时间关闭时间
id
1 1 08:00:00 13:00:00
2 1 16:00:00 22:00:00

从逻辑上讲,一次只能保存一个实体,不仅在应用程序级别,而且在DBMS级别,因此该规则告诉您数据无效是正确的,因为它一次只验证一个实体。

我认为你要么需要某种状态来处理这一问题,要么需要某种验证和应用程序规则的混合,即你需要验证无状态数据以及有状态/持久化数据,不仅不受干扰的数据集中的所有时间都需要彼此有效,它们还需要对数据库中已经持久化的数据有效。

我想,如何最好地解决这个问题还有很大的争论空间,应用程序规则中还没有允许验证多个实体的接口,所以无论你如何解决问题,都可能是某种折衷/变通方法。

由于这最终是关于数据库中的数据,所有验证都应该在事务中进行,所以我想最好将事情保留在应用程序规则中。我可以想到的一种肮脏的方法是,将所有实体的ID传递到保存过程中,并在您的规则中排除所有尚未检查(因此保存(的ID,这样后续检查将针对已经持久化的先前实体运行,这意味着最终您将检查所有记录的有效性。

这里有一个例子-我不知道你的关联设置,所以我只假设你用关联的BusinessHours保存Stores,但确切的设置并不重要,它应该只是显示如何构建和传递自定义选项:

// ...
$store = $this->Stores->patchEntity($store, $this->request->getData());
$businessHourIds = new ArrayObject(
collection($store->business_hours)
->extract('id')
->filter()
->indexBy(function ($value) {
return $value;
})
->toArray()
);
// $businessHourIds would represent an array like this, where both
// the key and the value would hold the ID: [1 => 1, 2 => 2, 3 => 3, ...]
if ($this->Stores->save($sore, ['businessHourIds' => $businessHourIds])) {
// ...
}
// ...

重要的是,它是一个ArrayObject实例,这样它的状态将在每个营业时间实体的多个不同save()调用中保持不变。然后你的规则可以做一些类似的事情:

function (BusinessHour $entity, array $options): bool {
if (!isset($options['businessHourIds'])) {
return false;
}

// If the entity isn't new, its ID must be present in the IDs list
// so that it can be excluded in the exists check
if (
!$entity->isNew() &&
!isset($options['businessHourIds'][$entity->id]
) {
return false;
}
// the current entries opening time must be smaller than its closing time
if ($entity->opening_time >= $entity->closing_time) {
return false;
}
$conditions = [
'id NOT IN' => (array)$options['businessHourIds'],
'store_id' => $entity->store_id,
'day' => $entity->day,
'closing_time >' => $entity->opening_time,
'opening_time <' => $entity->closing_time,
];

// remove the current entity ID from the list, so that for the next
// check it is not being excluded, meaning that all following business
// hours will have to be valid according to this entity's data
unset($options['businessHourIds'][$entity->id]);
return !$options['repository']->exists($conditions);
}

因此,这是未经测试的,理想情况下它会起作用,但它主要是为了说明我所说的内容。还要注意的是,我已经减少了打开/关闭时间条件,假设关闭时间必须大于打开时间,理论上这种检查应该涵盖所有可能的重叠。

最新更新