Karl Rixon

Archive for the ‘PHP’ tag

counterCache and deleteAll: How to Make Them Friends

with 5 comments

counterCache is one example of why I like CakePHP so much. It’s a little detail which puts a smile on my face. In case you haven’t come across it before, it is (as it’s name suggests), a cached count of the models which belong to another model. For example, applied to the scenario where a Group hasMany Users, counterCache can be used to keep track of the number of users belonging to each group without having to use lots of User->count() calls.

This works by having a field in the ‘parent’ model’s table which stores the count. Every time a child is added to or removed from the parent, this field is incremented or decremented accordingly. You can even set a scope for updating this value using counterScope. For example, you may want to only account for Users which have their deleted field set to 0.

I won’t go in to how to set this up because it is all explained in the book, and is not what this article is about. Suffice to say it all works very well. Until you come to use deleteAll() that is. deleteAll (again as it’s name suggests) takes a condition string, and deletes all records which match it. What it doesn’t do however is update the counterCaches as it goes. This can leave you with out of synch counts in the database.

One reaction to this might be to just call Model->del() in a loop – but this seems quite cumbersome and inefficient to me. Luckily however there is a method of the Model class called updateCounterCache which can fix everything. It is pretty simple to use, but the description in the API of the parameters is a little confusing so hopefully I can clarify with an example.

Going back to the example of Users and Groups, let’s assume I’ve just run deleteAll() and got rid of a few Users from Group 3. Running the following will keep the counter in the Groups table correct.

1
$this->User->updateCounterCache(array('group_id' => 3));

You can also update multiple counts in different parent models too. Assuming you want to update the count of all Users in Group 3, and all Users in Country 28:

1
2
3
4
$this->User->updateCounterCache(array(
    'group_id' => 3,
    'country_id' => 28
));

Written by Karl

May 4th, 2009 at 12:48 am

Posted in CakePHP

Tagged with ,