问题
我遇到了一个问题,即如何最好地在表上实现业务规则,该规则应适用于在相互了解的情况下创建的实体。从我迄今为止的研究来看,我怀疑这违背了CakePHP的内部工作方式,很可能我错过了框架的另一个功能。
有问题的实体具有hasMany
关联,通过该关联保存数据。例如,UsersTable
和Questionnaires
通过UsersQuestionnairesTable
相关联。因此,我注意到UsersQuestionnairesTable
的buildRules
方法运行n
次,其中n
是正在创建的关联实体的数量。
目标
我的目标是应用一个构建规则,确保在用户不存在其他UsersQuestionnairesTable
记录的情况下,请求数据中的UsersQuestionnaire
行中的一行(并且只有一行(被标记为default: true
。
目前的结果实际上是,通过在UsersQuestionnairesTable::buildRules
中应用这一点,在尝试创建具有通过UsersQuestionnairesTable
与其关联的一些Questionnaires
的User
时,只有当它封送到实体中的有效载荷的第一个数据行具有default: true
时,它才会通过验证。因此,它不适用于多个实体的创建,因为它将在作为default: false
的第二行失败,从而不创建User
。
我所理解的/已经尝试过的
据我所知,Table
类的buildRules
方法是执行应用于实体的应用程序逻辑规则的有用地方。例如,一封电子邮件在数据库中是唯一的。
从CakePHP 3.x食谱>验证数据>应用应用程序规则
在验证确保数据的形式或语法正确的情况下,规则侧重于将数据与应用程序和/或网络的现有状态进行比较。
这些类型的规则通常被称为"域规则"或"应用程序规则"。CakePHP通过在实体持久化之前应用的"RulesCheckers"公开了这个概念。一些示例域规则是:
- 确保电子邮件的唯一性
- 状态转换或工作流步骤(例如,更新发票状态(
- 防止修改软删除的项目
- 强制使用/费率限制上限
我想在创建过程中对实体应用类似的规则,但知道请求数据中的其他实体,这样我就可以访问正在创建的所有UsersQuestionnairesTable
实体来检查它们的deafult
值,如果没有发现true
,则使函数失败,并且不创建所有实体。
目前,在创建过程中但在保存之前(尽管不正确(应用于每个实体的条件规则应该说明所需的最终目标。
用户问卷表:
public function buildRules(RulesChecker $rules)
{
$enforceFirstAsDefault = function ($entity) {
$count = $this->find('all')
->where(['UserQuestionnaires.user_id' => $entity->user_id])
->andWhere(['Questionnaires.type_id' => 1])
->contain(['Questionnaires'])->count();
if ($count == 0 && !$this->containsDefault($entity)) {
return false;
}
return true;
};
$rules->add($enforceFirstAsDefault, [
'errorField' => 'no_default_identified',
'message' => 'A default questionnaire is required')
]);
}
...
private function containsDefault($entities): bool
{
foreach ($entities as $entity) {
if ($entity->is_default) {
return true;
}
}
return false;
}
由于希望在创建之前失败,模型上的buildRules
认为定位此逻辑的最合适位置仅适用于此模型:
- 行为更适合模型之间的常见行为
- 虽然你可以用
$event->getData()
访问beforeMarshal
中的数据,但我知道这种方法更适合持久化之前的数据操作,因此可能不合适满足我的需求 - 在许多可能直接或通过关联负责创建
UsersQuestionnaires
记录的控制器中具有此功能,感觉不那么枯燥和SRP,因为它需要Users::create
(例如(知道并基于取决于UsersQuestionnaires
有效载荷数据中的一行是否包含default: true
,而不管它是否由某种可重复使用的助手包装
问题
关于应用buildRules
,我是否缺少一些东西,它将允许检查作为此请求的一部分将要或已经编组的所有实体,从而只有在创建了所有实体后,验证才会失败或通过?
也许是一种用更多请求上下文覆盖/重载buildRules
的方法?还是这种业务逻辑更适合在另一个位置或使用框架的不同功能?
您的规则函数可以接受第二个参数,该参数接收传递给save
函数的选项。所以在你的控制器中,
$this->UsersQuestionnaires->saveMany($entities, ['entities' => $entities]);
然后在你的桌子上:
$enforceFirstAsDefault = function ($entity, $options) {
// Use $options['entities'] here to access the set of entities being saved
}