CakePHP 3.x:将应用程序规则应用于多个实体



问题

我遇到了一个问题,即如何最好地在表上实现业务规则,该规则应适用于在相互了解的情况下创建的实体。从我迄今为止的研究来看,我怀疑这违背了CakePHP的内部工作方式,很可能我错过了框架的另一个功能。

有问题的实体具有hasMany关联,通过该关联保存数据。例如,UsersTableQuestionnaires通过UsersQuestionnairesTable相关联。因此,我注意到UsersQuestionnairesTablebuildRules方法运行n次,其中n是正在创建的关联实体的数量。

目标

我的目标是应用一个构建规则,确保在用户不存在其他UsersQuestionnairesTable记录的情况下,请求数据中的UsersQuestionnaire行中的一行(并且只有一行(被标记为default: true

目前的结果实际上是,通过在UsersQuestionnairesTable::buildRules中应用这一点,在尝试创建具有通过UsersQuestionnairesTable与其关联的一些QuestionnairesUser时,只有当它封送到实体中的有效载荷的第一个数据行具有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
}

最新更新