Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CakePHP 3: Accessing current user in model

Since i'm new to CakePHP, I have simple problems I cannot figure out.

I use CakePHP 3.4. I try to write a simple logger functionality. Every change applied to a record, I want to be logged to the ChangeLog model.

Using afterSave() event, I have following code:

public function afterSave($event, $entity, $options) {
  $logTable = TableRegistry::get('ChangeLogs');
  foreach ($entity->getDirty() as $key) {
    if($key != 'modified') {
      $record = $logTable->newEntity();
      $record->previous_value = $entity->getOriginal($key);
      $record->new_value = $entity[$key];
      $record->table_name = 'Stars';
      $record->column_name = $key;
      $record->row_id = $entity->id;
      $record->user_id = [what should i put here?]
      $record->user_id = $_SESSION['Auth']['user']['id'];
      $logTable->save($record);
    }
  }

It works well, but I also want to know which user performed operation and I don't know how can I obtain current user in the Model.

I try to avoid passing argument in controller, because I want user to be detected automaticly, and as a developer I don't want to remember about it every time I try change/add new functionalities in controller.

like image 741
ziemowit Avatar asked Sep 24 '26 01:09

ziemowit


2 Answers

Do not fiddle with superglobals directly in CakePHP, this will surely bite you at some point, especially in the test environment! Always use the abstracted methods (like the session object) to access such data!

That being said, you could use events to inject the current user into the model callback/event flow. For example register globally to Model.afterSave, and pass the current user into the options.

Here's a basic example to demonstrate the principle. Imagine somthing like this in your app controller:

use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\Event\EventManager;

// ...

public function initialize()
{
    parent::initialize();

    // ...

    EventManager::instance()->on(
        'Model.afterSave',
        ['priority' => -1],
        function (Event $event, EntityInterface $entity, \ArrayObject $options) {
            // retrieve the user id from the auth component
            $options['user_id'] = $this->Auth->user('id');
        }
    );
}

Given the priority of -1 (the default priority is 10) it will be invoked before the model callback for that event, so that in your table class you'll have access to user_id via the $options argument.

$record->user_id = $options['user_id'];

For something more reusable you'd probably use a custom listener class. Also check out events like Auth.afterIdentify, Model.initialize, and Controller.intialize/startup, these could be leaveraged to register your model events listener and to retrieve the current user.

See also

  • Awesome CakePHP > Auditing / Logging
  • Cookbook > Events System
  • Cookbook > Events System > Registering Listeners
  • Cookbook > Events System > Establishing Priorities
  • Cookbook > Database Access & ORM > Table Objects > Lifecycle Callbacks
  • Cookbook > Controllers > Request Life-cycle Callbacks
like image 155
ndm Avatar answered Sep 25 '26 14:09

ndm


This solution seems to allow you to pass the logged in user into the model layer:

https://github.com/UseMuffin/Footprint

It is not hooked into the model layer through events like the solution above.

like image 44
space97 Avatar answered Sep 25 '26 15:09

space97



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!