Accessing User Sessions From Models (or Anywhere) In CakePHP REVEALED!
The latest version of this code can be found at http://github.com/mcurry/cakephp_static_user/
"How do I access the logged in user in a model?" I've seen this question a bunch of times. I've tried numerous solutions, but never really liking any of them. The guys over at debuggable.com have hinted at a User::get('id') method twice, but never revealed the details.
Early Attempts
This question has shown up on the Google Group. Nate (one of the Cake developers) responded:
There are two right ways of doing this. One is to make the session a formal part of your domain model, i.e. model it. This allows it to interact with other models at the model level.
I think I get what Nate is saying here. Make a Session model without a table, set relationships to this model in any other models that may use it (or do I just call it statically?) and (somehow) set the data from your session to the model in app_controller. Assuming I'm understanding this correctly (there's a good chance I'm not), I wasn't crazy about this approach. How do you get the session data to the model? How do you set up the relationships when one of the models is tableless?
Nate's other approach was to set the session/user as a variable in the model with this code in the app_controller:
function beforeFilter() {
$this->{$this->modelClass}->setUserState($this->Auth->user());
}
I used this approach for awhile, but had issues trying to access the user in cases where the model wasn't the "primary" one. The code above only sets the user state for the one model class, not any related models. You could loop through all relationships recursively to make sure the data is set down the line, but ehhh...
What I'm Using Now
Here's what I have. It's not perfect, but it's the best solution I've come up with so far. I liked the "User::get()" syntax used in the debuggable.com posts, so I worked from there. In your User model:
httpc://github.com/mcurry/cakephp/raw/2b78e2882718cc8a94b4e092897d3fec90ff5510/snippets/static_user/user.php
In AppController, beforeFilter method (assuming you are using the Auth component):
App::import('Model', 'User');
User::store($this->Auth->user());
How It Works
To get the currently logged in user's id:
User::get('id');
To get the currently logged in user's username (assuming you have a field "username" in your users table):
User::get('username');
Any other fields in your user table can be retrieved in the same manner. Also if you store related model data in your user session it can be retrieved:
User::get('Model.fieldname');
Issues
To get the "User::get()" syntax to work this block has to be placed in the User model, but it feels slightly misplaced there.
Uses
This has code has come in really handy. I use it a behavior with a beforeSave callback to set the created by user id and modified user id. Likewise you can have a behavior that automatically filters rows for index pages to only show records appropriate for the logged in user. It also works in anywhere in your Cake app. In views you can use this instead of $session->read('Auth.User.id').
Update
I missed that Cake already has a Model::set method. To prevent the conflict the method to set the user has been changed to "store". I also added an App::import in the AppController->beforeFilter to make sure the user model is already included.

9 Comments
class SessionViewBehavior extends ModelBehavior {
function __validateSessionKeys($path) {
if (is_string($path) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $path)) {
return $path;
}
return false;
}
function sessionCheck(&$model, $path) {
$var = $this->__validateSessionKeys($path);
if (empty($var)) {
return false;
}
$result = Set::extract($_SESSION, $var);
return isset($result);
}
function sessionRead(&$model, $name = null) {
if (is_null($name)) {
return null; //$this->__returnSessionVars();
}
if (empty($name)) {
return false;
}
$result = Set::extract($_SESSION, $name);
if (!is_null($result)) {
return $result;
}
return null;
}
}
Not quite DRY, but as I tend to think of the session as 'just another data store', then I'm comfortable with it being a behaviour.
The concern I always had with the approach you're using is accessing $_SESSION directly in the model/behavior. I've done it before, but always felt dirty afterwards :)
Plus, I think (never actually tried), if you use Cake's DB sessions then $_SESSION may not have the information you're expecting. Admittedly, this probably isn't a factor for most people.
The access to $_SESSION is identical to that in SessionComponent (hence not very DRY). Also, I'm only accessing the Auth.* parts (after the login redirect), so timing hasn't (yet) been an issue.
Unless cake starts using a real model for Session (or a datasource), then we're a bit stuck between a rock and a hard place - I'm not particularly comfortable with any of the approaches I've seen so far (even mine :)
I'm currently using the following snippets (don't know if they're the 'right' way, but they work):
In AppController->beforeFilter() place this line:
$this->set('Auth', $this->Auth->user());
Note the parenthesis after user!
This makes the User model available in both views and controllers. To access the User data from a view, use:
echo 'Logged in as . ' $Auth['User']['username'];
To access the data from a controller, use:
$user = $this->Auth->user();
$name = $this->Auth->user()['User']['username']
In User model declare static var:
class User extends AppModel {
...
var $name = 'User';
static $auth;
...
}
In AppController:
// App::import('Model', 'User');
function beforeFilter() {
$auth = $this->Auth->user();
$this->set(compact('user'));
User::$auth = $auth;
}
Now it accessible both in views ($auth var) and models (User::$auth var)
I recently came across a situation where I needed to delete session data in an afterSave callback within a behavior (and no, the situation didn't allow this to be done within a controller).
Here's what I did to accomplish such a task.
$CakeSession =& new CakeSession(null, false);
$CakeSession->del('Module.key');
Add new comment