Cache Elements Individually For Each User.

By Ketan (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:

Download code
// 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:

Download code
// 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:
Download 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

Download code
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 461

CakePHP Team Comments Author Comments
 

Comment

1 Cache Elements Individually For Each User

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 <user_dir>/<user_file> or all of them dumped in one /tmp directory?

thanks,
mandy.
http://mandysingh.blogspot.com
Posted Jul 20, 2007 by Mandy Singh
 

Comment

2 Why would that be a problem.


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 <user_dir>/<user_file> or all of them dumped in one /tmp directory?


The approach you mentioned will create million directories and million files!! However, just so you know, linux is extremely efficient with file io and this should not cause a major issue. However, if you have windows then I can forsee problems much more.

In any case, once you are at million user base, you will have to make more custom solutions and much more effort would be required.

Ketan
Free Classifieds - eClassifieds4U
Posted Jul 20, 2007 by Ketan
 

Comment

3 Code not working anymore

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 Jul 28, 2007 by Rady
 

Comment

4 similar approach but modified the view code

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 Aug 11, 2007 by Ritesh Agrawal
 

Comment

5 Want it to work with older cake

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 Aug 13, 2007 by alan blount