0

I use AuthComponent with CakePHP 3.8 and now I need to do some logic in Model buildRules method but for this I need to get the current user ID. Is there any way to pass/retrieve it without using hacks such as accessing directly from the session. I know that it is possible to pass id via validator from controller as described in CakePHP's documentation https://book.cakephp.org/3/en/core-libraries/validation.html#using-custom-validation-rules

And it works for validation, however, I am unable to access the validator from the inside of build rules.

When I do as described in here, I get an empty object. https://book.cakephp.org/3/en/orm/validation.html#using-validation-as-application-rules

It seems that I am able to attach new validation rules but unable to retrieve the "Passed" provider to get the User ID.

It seems a trivial thing but a I spent quite a few hours trying to get the id in a proper way.

gagnav
  • 48
  • 7

2 Answers2

0

OK, After working a bit more, I found how to retrieve user_id inside build rules. Might be helpful to someone.

Do this in the controller

         $this->ExampleModel->validator('default')->provider('passed', [
            'current_user' => $this->Auth->user('id')
         ]);

And then put this in you buildRules method

public function buildRules(RulesChecker $rules)
{
    $user_id = $this->validator('default')->provider('passed')['current_user'];
    $rules->add(
       function ($entity, $options) use($user_id) {

         //return boolean for pass or fail
        },
        'ruleName',
        [
          'errorField' => 'some_id',
          'message' => 'Some ID field is inconsistent with App logic.'
        ]
    );

    return $rules;
}
gagnav
  • 48
  • 7
0

The proper way to handle this is usually using either events, or saving options.

For example to make the ID available for the application rules of all tables, you could do something like this:

$this->getEventManager()->on('Model.beforeRules', function (
    \Cake\Event\Event $event,
    \Cake\Datasource\EntityInterface $entity,
    \ArrayObject $options
) {
    $options['current_user'] = $this->Auth->user('id');
});

It would be available in the $options argument of the rule accordingly, ie as $options['current_user'].

For a specific save operation you can pass it to the options of the save() call:

$this->ExampleModel->save($entity, [
    'current_user' => $this->Auth->user('id')
]);

There's also plugins that can help you with it, for example muffin/footprint.

ndm
  • 59,784
  • 9
  • 71
  • 110
  • This might be the proper way, but I prefer passing through the validator more, as it makes it more clear to anyone who reads the code the purpose, otherwise if it is passed through the save method it might get confusing for what purposes it is passed for. – gagnav Jun 12 '20 at 09:36
  • @user3785288 I don't think that's how it would play out, people familiar with CakePHP would notice there's something wrong, and people that are unfamiliar will "learn" the wrong way to solve the problem. Also the code would break as soon as the validator would enforce the documented argument type, as it expects `object|string`, an array is not a valid validation provider, code analyzer tools will complain as the code violates the API contract. – ndm Jun 12 '20 at 10:30
  • The example code that I wrote is taken from CakePHP's official documentation. I guess one also can use eventManager to pass current user to the validation object but this way how it is described in official documentation. Furthermore, plugging in the eventManager code anywhere in the app to me is semantically no different than just statically calling any object anywhere. – gagnav Jun 12 '20 at 13:09
  • @user3785288 Either the cookbook needs to be changed, or the code and the API docs needs to be updated to specifically support arrays (there aren't even any tests for that) - I'll open a ticket. – ndm Jun 12 '20 at 14:00
  • @user3785288 https://github.com/cakephp/cakephp/issues/14703 – ndm Jun 15 '20 at 08:03