【问题标题】:CakePHP 3.x: Applying application rules to multiple entitiesCakePHP 3.x:将应用程序规则应用于多个实体
【发布时间】:2020-04-06 16:27:12
【问题描述】:

问题

我有一个与如何最好地在表上实施业务规则有关的问题,该表应该适用于在彼此了解的情况下创建的实体。从我目前的研究来看,我怀疑这与 CakePHP 的内部工作原理背道而驰,而且我可能错过了框架的另一个特性,它允许这样做。

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

目标

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

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

我理解/尝试过的

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

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

在验证确保数据的形式或语法正确的情况下,规则侧重于将数据与应用程序和/或网络的现有状态进行比较。

这些类型的规则通常被称为“域规则”或“应用程序规则”。 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 记录的控制器中具有此功能,无论是直接还是通过关联都感觉不那么 DRY 和 SRP,因为它需要 Users::create (例如)知道并采取行动 UsersQuestionnaires 有效负载数据中的一行是否包含default: true,无论它是否由某种可重用的助手包装。

问题

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

也许是一种用更多请求上下文覆盖/重载buildRules 的方法?还是这种业务逻辑更适合在其他位置使用或使用框架的不同功能?

【问题讨论】:

    标签: cakephp cakephp-3.0


    【解决方案1】:

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

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

    然后在你的桌子上:

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

    【讨论】:

    • 效果很好,谢谢。不知道从save 过滤到规则的东西。您能否链接到详细说明此内容的食谱/API 文档的章节?如果这是我在旅行中错过的东西,我们深表歉意,但感谢您抽出宝贵时间提供帮助!
    • 是的,我没有看到规则、验证或保存部分中明确提到的连接。可能是在文档存储库中提出的好问题?
    猜你喜欢
    • 2018-05-08
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多