CakePHP 3 - Switch BuildRules set

113 Views Asked by At

I am making an application where users can create activities and other users can subscribe to them. New activities have to be approved through an admin panel, to which only admins have access.

Activities that been approved should be locked, such they cannot be changed by their user. Only through the admin panel approved activities should be editable.

To implemented this we made this buildRule:

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 instead of validation rule because the validation should also kick in when the approved field is not modified and hence is not part of the validation.)

The rule given above prevents admins from modifying the activities as well, so my question:

Can you switch between builRules in your table, like you can change the validation method?

2

There are 2 best solutions below

0
On BEST ANSWER

Not really, there is an Model.buildRules event that can be used to add rules, but that's about it.

I'm not sure that it's an overly good idea to jiggle with multiple application rules depending on state outside of the model. What you are trying to achieve there sounds like access control, ie authorization, and I'd suggest to implement checks accordingly.

Have a look at 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 plugin.

If your authorization is really very basic, ie "admin area = edit allowed", and "non-admin area = edit NOT allowed", ie there's maybe just this one point in your application where a check would be required, then you could probably get away with something less sophisticated, like for example passing options into the saving process. The options will be passed into the rules, where you could check them accordingly, ie something like this:

$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');
}

That way saving approved entities would only work when true is passed via the updateApproved option. As already mentioned, that's not really an overly good solution, in any case I'd advise to have a look at the authorzation plugin and learn how to implement authorization properly.

See also

0
On

I guess @ndm's solution is the more official one. I ended up with a different solution, that I want to try for a bit first.
Basically I added a flag to the Activities model:

class ActivitiesTable extends Table
{

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

    // ...

And made the rules building dependent on it:

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

In a controller you can then toggle the model to admin mode and save an entity that would otherwise fail:

$this->Activities->_admin = true;

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