Cache Elements Individually For Each User.

This article is also available in the following languages:
By ketan
Caching elements in general has been discussed before on bakery and this article takes caching of an element to a higher level. This article explains how to cache elements individually for each user.
An article on how to cache an element has been published earlier on bakery at http://bakery.cakephp.org/articles/view/optimizing-your-cakephp-elements-and-views-with-caching. This article is an enhancement to the above mentioned article. So make sure you are through with the other one before getting on this.

There are many situations where you would want to cache an element individually for each user. Cases like having separate tagClouds, todolists, etc for each user. Remember, if you are calling 'requestAction' in your elements then it may be a good idea to cache the element as sometimes 'requestAction' could get expensive with lots of them.

For this article, I will assume that we are having a todo element as follows:


// Todo Element

$todoLists = $this->requestAction('todoLists/getList');


echo '<h2>ToDo List</h2>';
echo '<ul>';

foreach($todoLists as $todoList)
{
   echo '<li>'.$todoList['name'].'</li>';
}
echo '</ul>';

And in one of my views I call this element as follows:


// Generic way to cache element
$this->element('todo', array('cache'=>'+1 day'));

If I call element using the above call, what happens is that Cake will cache the element 'todo' as app/tmp/cache/views/element__todo for 1 day. But this cache will be same for all users as the name of the element and cache file generated is the same.

First you must understand how the cache file for element is named. It is as 'element_{plugin}_{name of element}'. In our case, as we didnot specify the plugin, it was considered null, and you see two consecutive 'underscores' in the name of the cache file (element__todo).

Taking the naming convention to our advantage, we trick Cake to do our job. Take a look at following code:

// Individually cache the element for each user.
$userId = 3; // For example
$this->element('todo', array('cache'=>'+1 day', 'plugin'=>$userId));

Woila, you are done. You just tricked Cake to create a separate cache
file for each user. By specifying the plugin to be userId, Cake
creates a separate cache file with the name 'element_3_todo'.

So now for each user, cake will create a separate cache file. Hang on,
don't you want to know how to delete this cache file if the user
updated his todo list?? In your TodoList Model, update the afterSave
method as follows


function afterSave()
{
  $userId = $this->data['TodoList']['user_id'];
  $this->clearCache('element_'.$userId.'_todo', 'views', '');
  parent::afterSave();
}

That's it, whenever user updates the todo list, cake will now
automatically clear the cache for that user.

This is my first article and I would really want people to post some
constructive feedback and comments. Let me know how you feel about
this article.

Ketan Patel

Comments

  • Posted 08/16/08 08:47:16 AM
    Taken from http://book.cakephp.org/view/561/caching-elements:

    Good idea with your article. I believe errors now occur because of the nonexistence of that plugin, and guess what! We don't have to trick Cake anymore. Here is the old version that tricks Cake:

    // Individually cache the element for each user.
    $userId = 3; // For example
    $this->element('todo', array('cache'=>'+1 day', 'plugin'=>$userId));

    Here is the new version that does not trick Cake:
    <?php
    echo $this->element('helpbox', array('cache' => array('key' => $userId'time' => '+1 day'), 'exampleVariable_Group_id' => $user['Group']['id']));

    And best of all, you feel like you haven't broken any laws. Thanks for the article!
  • Posted 07/08/08 11:16:27 AM
    i get a cachfile called
    "element_cache_plugin_1posts"
    instead of "element_{plugin}_{name of element}" from above

    somehow the underscore moved one to the right
    is this a bug in the 1.2 v. or did the cache function change?
    mark
  • Posted 08/13/07 09:34:04 AM
    If you're looking for a way to do something like this which works with cakephp 1.5...

    http://bakery.cakephp.org/articles/view/ecache-easy-per-user-or-per-anything-cache-of-html-or-arrays
    (pending approval, at the moment)
  • Posted 08/11/07 11:56:46 AM
    Rather than tricking CakePHP, I solved the same problem by introducing a new variable, "uniqueID", in lib/view/view.php. Although this is not the best approach as I am directly modifying cakephp core code, it atleast avoids problems as reported by Rady.

    Here is the modified code for lib/view/view.php.

    function element($name, $params = array()) {
    if(in_array('cache', array_keys($params))) {
    $expires = '+1 day';
    if($params['cache'] !== true) {
    $expires = $params['cache'];
    }
    if($expires) {
    $plugin = null;
    if(isset($params['plugin'])) {
    $plugin = $params['plugin'];
    }
    $cacheFile = 'element_' . $plugin .'_' . convertSlash($name);
    $cacheFile = 'element_' . $plugin .'_' . convertSlash($name);
    if(in_array('uniqueID', array_keys($params))){
    $uniqueID = $params['uniqueID'];
    $cacheFile ='element_' . $plugin .'_'. $uniqueID .'_'. convertSlash($name);
    }


    $cache = cache('views' . DS . $cacheFile, null, $expires);
    if($cache) {
    return $cache;
    } else {
    $element = $this->renderElement($name, $params);
    return cache('views' . DS . $cacheFile, $element, $expires);
    }
    }
    }
    return $this->renderElement($name, $params);
    }

    So to cache todo list (from above example) for a specific user, the call would like this
    $this->element('todo', array('cache'=>'+1 day', 'uniqueID'=>$userId));

    Regards
  • Posted 07/28/07 02:53:37 AM
    Since commit 5463 in the branch of 1.2.x.x (https://trac.cakephp.org/log/branches/1.2.x.x/cake/dispatcher.php) this approach won't work anymore. The dispatcher will search for the plugin, which obvisiously cannot find.
  • Posted 07/20/07 01:45:33 AM
    nice handy article.

    ketan - my only concern is that if i have a million user in my database, i will be creating million tmp files on disk - is this a good approach? should files be stored in / or all of them dumped in one /tmp directory?

    thanks,
    mandy.
    http://mandysingh.blogspot.com
  • Posted 07/15/07 09:47:17 AM
    Nice Article. Please remove the signature with a link to a foreign site so I can publish it.

Comments are closed for articles over a year old