CakePHP 2.0 – Storing Sessions in the database and retrieving online users

Notice

An updated version of this article is available here: CakePHP 2.8.x: Storing Sessions in the Database Revisited

Preface

Because I want to make this example as easy to understand for everyone, I will write it in English. I’m not a native speaker / writer so there might be some grammar errors for which I apologize in advance.

Preamble

What I will be covering today is how to configure your CakePHP 2.0 system to use your database to store user sessions, if you have one obviously. This could be useful to manage multiple sessions from different devices / ip addresses and to make sure no one else is signed in when you sign in somewhere else. Those parts I won’t be covering today, because that wouldn’t make this a small-tutorial =]. What I will be covering is something I find to be extremely handy in some situations, and that’s a method that shows you who’s online by checking recent session data.

First things first, creating the database table

To store the sessions in the database, we are going to use the default table structure for cake sessions. The following code will create the correct table in your database. Just execute it in something like phpMyAdmin or directly in your sql console

CREATE TABLE IF NOT EXISTS `cake_sessions` (
`id` VARCHAR(255) NOT NULL ,
`data` TEXT NULL ,
`expires` INT UNSIGNED NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;

The SQL code creates a simple database table with a varchar as identifier. The reason that it’s a varchar is because of the way a session id is made.

Configuring Cake

Time to configure CakePHP in such a way that it knows it has to store the sessions in the database instead of using php or a filesystem. At line 138 of the file app/Config/core.php you’ll find the start of how CakePHP handles sessions. Scroll down till you find the following:

Configure::write('Session', array(
    'defaults' => 'php'
)); 

If you haven’t completely stripped the comments out of your core.php file, it describes what kind of options you have to store sessions. We are going for the database option in this case. Change the above code into the following:

Configure::write('Session', array(
    'defaults' => 'database',
    'handler' => array(
        'model' => 'cake_sessions'
    )
));

The handler option is not required if you would like to stick with the default model name. If you want to use a custom method for handling sessions, you could create a custom model. I’ll stick with the default one.

The structure of the model used is pretty basic:

<

pre class=”prettyprint lang-php”>
Now save this file in your Models folder as cake_sessions.php and we are good to go! Do not close the file with ?>. The reason is the following:

The PHP closing tag on a PHP document ?> is optional to the PHP parser. However, if used, any whitespace following the closing tag, whether introduced by the developer, user, or an FTP application, can cause unwanted output, PHP errors, or if the latter are suppressed, blank pages.

After you have done this, you might want to refresh your cake system by signing out / press ctrl-f5 to fully refresh the sessions stuff. This should do the trick, but in the unfortunate event this raises any errors, please retrace your steps by going through the details above one more time.

Retrieving who’s online

Well now, that wasn’t that hard was it? Let’s try adding a bit more usage. We are going to introduce a method to the cake_sessions.php Model called getActiveUsers(). This will return a select set of data from active sessions. Let’s get ready to add some code to the model.

<?php
class cake_sessions extends AppModel{
    public function beforeSave($options = array()){
    }

    public function getActiveUsers(){
        $minutes = 10; // Conditions for the interval of an active session
        $sessionData = $this->find('all',array(
            'conditions' => array(
                'expires >=' => time() - ($minutes * 60) // Making sure we only get recent user sessions
            )
    ));

    $activeUsers = array();
    foreach($sessionData as $session){
        $data = $session['cake_sessions']['data'];
        // Clean the string from unwanted characters
        $data = str_replace('Config','',$data);
        $data = str_replace('Message','',$data);
        $data = str_replace('Auth','',$data);
            $data = substr($data, 1); // Removes the first pipe, don't need it

        // Explode the string so we get an array of data
        $data = explode('|',$data);

        // Unserialize all the data so we can use it
        $auth = unserialize($data[2]);

        // Check if we are dealing with a signed-in user
        if(!isset($auth['User']) || is_null($auth['User']['id'])) continue;

        /* Because a user session contains all the data of a user 
             * (except the password), I will only return the User id 
             * and the first and last name of the user */

        /* First check if a user id hasn't already been saved 
             * (can happen because of multiple sign-ins on different 
             * browsers / computers!) */

        if(!in_array($auth['User']['id'],$activeUsers)){
                $activeUsers[$auth['User']['id'] = array('first_name' => $auth['User']['first_name'], 'last_name' => $auth['User']['last_name']; 

             /* Keep in mind, your User table needs to contain 
              * a first- and lastname to return them. If not, 
              * you could use the email address or username 
              * instead of this data. */

        }
    }
    return $activeUsers;
    }
}

And there you have it. Try adding the cake_sessions model to your $uses variable in your AppController and calling the method via $this->cake_sessions->getActiveUsers(); inside your beforeFilter() method if you have created one. If you have a basic knowledge of CakePHP you know how to call this method from different controllers and stuff. If not, go to book.cakephp.org!

Epilogue

I hope you enjoyed this small-tutorial and learned something. If it helped one person, I’m satisfied with the result. In case you do have any questions, please feel free to to respond in the comments below and I’ll see what I can do!

  • Erick

    Thank You, nice tutorial.

  • Adrien

    Hoi Thomas,
    Binnenkoort een article op Symfony2?
    Cheers,
    Adrien.

    • thomasvnl

      Hej Adrien, wie weet 😉 Geen idee of ik het zo goed vraag, maar: Avez-vous une adresse e-mail où je peux vous joindre? Je weet mijn werk mail denk ik wel, daar kan je hem heen sturen ^^ ben wel benieuwd naar je scriptie voortgang o.a.

      • Adrien

        Done!
        Ps: je frans is perfekt 😉

  • Sudhir Pandey

    Thanks have nice tutorial. I have used your code. I have query if I have login from two different browser. I want all login session expire except the current.

    • thomasvnl

      I will look into it and update the above code when done if I can find out how 😉

    • thomasvnl

      I’ve done my research and it seems to be possible. I’ve also written some new code, with which I am going to rewrite this article (because it’s a lot cleaner too!). I even had to add a fix to the CakePHP repository because it was ambiguous and wouldn’t allow relations between the Session model and the User model.

    • thomasvnl
  • Pingback: CakePHP 2.8.x: Storing Sessions in the Database Revisited | thomasv()