CounterCache in CakePHP 1.2 beta

Update
05.01.2008 At the moment the counterCache code is only executed on CREATE, not on UPDATE – a patch is under way
Hello,
Just a little turtorial to demonstrate the new model counterCache feature.
CounterCache is a very fancy little addition to our beloved CakePHP framework.
Simply put, all it does is make sure that your belongsTo associations always have an updated field with the count of elements belonging to the parent.
Lets use this simple example, just two models and controllers:

  • Posts (posts_controller) (hasMany PostComment)
  • PostComments (post_comments_controller) (belongsTo Post)

SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- our posts
CREATE TABLE `posts` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`post_comment_count` int(11) UNSIGNED DEFAULT NULL,
`title` varchar(100) NOT NULL,
`body` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
 
-- comments to our post
CREATE TABLE `post_comments` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`post_id` int(11) UNSIGNED NOT NULL,
`is_active` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
`name` varchar(100) NOT NULL,
`body` text NOT NULL,
PRIMARY KEY (`id`),
KEY `post_id` (`post_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

To keep things simple, we are just going to use scaffolding in controllers.
Controllers
Posts controller
Put in APP/controllers/posts_controller.php

1
2
3
4
5
<?php
// Posts controller
class PostsController extends AppController {
var $scaffold;
}

PostComments controller
Put in APP/controllers/post_comments_controller.php

1
2
3
4
5
<?php
// Post comments controller
class PostCommentsController extends AppController {
var $scaffold;
}

Models
Post model
Put in APP/models/post.php

1
2
3
4
<?php
class Post extends AppModel {
var $hasMany = array('PostComment');
}

PostComment model
Put in APP/models/post_comment.php

1
2
3
4
<?php
class PostComment extends AppModel {
var $belongsTo = array('Post' => array('counterCache' => true));
}

The interesting thing is the above PostComment model. A you can see, a new options key is added, counterCache and is set to (bool) true. This informs cake that it should use counterCache on the class (Post) it belongs to.
This is three possible variables to assign counterCache

  • (bool) true means enabled, and that cake should auto-generate the count field automagic
  • (string) fieldname tells cake to use your custom field in Post (posts) model and table.
  • any value that is considered ‘empty’disable the feature (this is default, NULL)

Its important to understand, that the field we specify here, is a field in the REMOTE model / table, not in the local table, so in this case, the field name is in posts table.!
As said earlier, cake will automagic generate the remote field name if you only provide a boolean true value, in this case the field will be named Inflector::underscore($this->alias) . ‘_count’ – meaning the alias of the LOCAL model (PostComment) and a suffix ‘_count’. All is that is converted to a lowercase, underscored value. When these rules are applied to our example above, the remote field in posts will be named post_comment_count (line 4 in SQL section)
With the code above, you have a very basic scaffolding setup running with couterCache enabled. Very simple stuff As always when its from the CakePHP team.
Using counterCache scope
As you might have noticed in the SQL table above, the post_comments table have a field called is_active (SQL line 14). This is a sample on how you could moderate your comments. Lets change a bit of code in our models to make this work.
Put in APP/models/post.php (Updated)

1
2
3
4
5
6
7
<?php
// Updated Post model
class Post extends AppModel {
var $hasMany = array('PostComment' => array(
'conditions' => array('is_active' => 1)
));
}

Notice that I have added a conditions array to the hasMany relation to PostComment.
This will tell cake to only fetch comments from PostComment where is_active is 1 (aka. is active).
Put in APP/models/post_comment.php (Updated)

1
2
3
4
5
6
<?php
class PostComment extends AppModel {
var $belongsTo = array('Post' => array(
'counterCache' => true,
'counterScope' => array('is_active' => 1)
));}

Notice that I have added another field to the Post relation called counterScope, this is the conditions that cake uses in the find(‘count’)query it creates to find the amount of comments to the Post. By adding this simple array, our counterCache now only count active comments
Easy done

Tags: , , ,