CakePHP 3 - 切换 BuildRules 集

CakePHP 3 - Switch BuildRules set

我正在制作一个应用程序,用户可以在其中创建活动,其他用户可以订阅这些活动。新活动必须通过管理面板批准,只有管理员才能访问。

已批准的活动应该被锁定,这样他们就不能被用户更改。只有通过管理面板批准的活动才能被 editable.

为了实现这一点,我们制定了这个构建规则:

public function buildRules(RulesChecker $rules)
{
    // Check if activity wasn't locked
    $rules->add(function ($entity, $settings)
    {
        // Return false if already approved before
        return !$entity->approved || $entity->isDirty('approved');
    }, 'alreadySubscribed', ['errorField' => 'name', 'message' => 'Activity has been approved and is now locked']);


    return $rules;
}

(BuildRule 而不是验证规则,因为当 approved 字段未被修改时验证也应该开始,因此不属于验证的一部分。)

上面给出的规则也防止管理员修改活动,所以我的问题:

您可以在 table 中的 builRules 之间切换,就像您可以更改验证方法一样吗?

不一定,有一个 Model.buildRules 事件可以用来添加规则,但仅此而已。

我不确定根据模型外部的状态调整多个应用程序规则是不是一个好主意。你试图在那里实现的目标听起来像是访问控制,即授权,我建议相应地实施检查。

看看 cakephp/authorization, it allows you to implement very flexible policies that can handle such cases. Alternatively there's the old school authorization functionality provided by the auth component, or the (highly undocumented) ACL 插件。

如果你的授权真的很基础,即“管理区=允许编辑”,以及“非管理区=不允许编辑",即您的应用程序中可能只有这一点需要进行检查,那么您可能会做一些不那么复杂的事情,例如将选项传递到保存过程中。这些选项将被传递到规则中,您可以在规则中相应地检查它们,即像这样的东西:

$options = [
    'updateApproved' => true
];
$Model->save($entity, $options);
function ($entity, $settings)
{
    if (isset($settings['updateApproved']) &&
        $settings['updateApproved'] === true
    ) {
        return true;
    }

    // ...

    return !$entity->approved || $entity->isDirty('approved');
}

只有在通过 updateApproved 选项传递 true 时,这种方式才能保存已批准的实体。如前所述,这并不是一个非常好的解决方案,无论如何我建议您查看 authorzation plugin 并了解如何正确实施授权。

另见

我想@ndm 的解决方案是更官方的解决方案。我最终得到了一个不同的解决方案,我想先尝试一下。
基本上我在活动模型中添加了一个标志:

class ActivitiesTable extends Table
{

    /** @var bool Flag whether admin is modifying the table */
    private $_admin = false;

    // ...

并使规则构建依赖于它:

    if (!$this->_admin)
    {
        $rules->add(function ($entity, $settings)
        {
            // ...

然后您可以在控制器中将模型切换到管理模式并保存否则会失败的实体:

$this->Activities->_admin = true;

$this->Activities->save($activity);