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

CakePHP 3.x: Applying application rules to multiple entities

问题

我有一个关于如何最好地在 table 上实施业务规则的问题,这些规则应该适用于在相互了解的情况下创建的实体。根据我迄今为止的研究,我怀疑这违背了 CakePHP 的内部工作原理,并且我可能错过了允许这样做的框架的另一个功能。

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

目标

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

目前的结果实际上是,通过在 UsersQuestionnairesTable::buildRules 中应用它,在尝试创建一个 User 并通过 UsersQuestionnairesTable 与之关联的一些 Questionnaires 时,它将仅当它编组到实体中的有效负载的第一个数据行具有 default: true 时才通过验证。因此,它不适用于创建多个实体,因为它会在第二行失败,如 default: false,因此不会创建 User.

我understand/have试过的

据我了解,Table class 的 buildRules 方法是强制执行适用于实体的应用程序逻辑规则的有用位置。例如,电子邮件在数据库中是唯一的。

来自CakePHP 3.x Cookbook > Validating Data > Applying Application Rules

Where validation ensures the form or syntax of your data is correct, rules focus on comparing data against the existing state of your application and/or network.

These types of rules are often referred to as ‘domain rules’ or ‘application rules’. CakePHP exposes this concept through ‘RulesCheckers’ which are applied before entities are persisted. Some example domain rules are:

  • Ensuring email uniqueness
  • State transitions or workflow steps (e.g., updating an invoice’s status).
  • Preventing the modification of soft deleted items.
  • Enforcing usage/rate limit caps.

我想在创建期间对实体应用类似的规则,但了解请求数据中的其他实体,这样我就可以访问正在创建的所有 UsersQuestionnairesTable 个实体来检查它们的 deafult 值,如果发现 none 为 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 在一个模型上感觉最合适的位置定位此逻辑仅适用于此模型,如:

问题

我在应用 buildRules 时是否遗漏了一些东西,这将允许检查将要或已经作为此请求的一部分编组的所有实体,这样验证只会失败或通过一次已创建所有实体?

也许是 override/overload 具有更多请求上下文的 buildRules 的方法?或者这种业务逻辑是否更适合在另一个位置或使用框架的不同功能?

您的规则函数可以采用第二个参数,该参数接收传递给 save 函数的选项。所以在你的控制器中,

$this->UsersQuestionnaires->saveMany($entities, ['entities' => $entities]);

然后在你的 table:

$enforceFirstAsDefault = function ($entity, $options) {
    // Use $options['entities'] here to access the set of entities being saved
}