CounterCache in CakePHP 1.2 beta

January 5, 2008 – 12:32 am
Table of Content [Hide]

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: , , ,

  1. 7 Responses to “CounterCache in CakePHP 1.2 beta”

  2. Will counterCache work for HABTM relationships, or self-joined tables (hierarchical categories, for example)? Can you give examples if this is possible? Thanks.

    By Webspin on Jan 6, 2008

  3. @1, counter cache will work for any model relation that is ‘belongsTo’ another model, other relations is not yet supported, and not quite sure if it will be.

    By Christian Winther on Jan 6, 2008

  4. Thanks, I did check the source and found out the same thing. I think I would have to make a custom beforeSave function in my child model to update the parent model and model the join table, and use belongsTo/hasA relationships to/from the join table and the (formerly) HABTM tables.

    If anyone has done this, and wouldn’t mind posting out how they’ve done it, that will be great.

    This was a great tutorial, and first place I’ve found any reference to counterScope. Good job.

    By Webspin on Jan 7, 2008

  5. Hey, nice tips, just figured these out myself right before finding your article. Regarding counterCache being generated only in CREATE, a $this->updateCounterCache in model’s afterSave should be enough to also handle UPDATEs.

    By none on Jan 18, 2008

  1. 3 Trackback(s)

  2. Jan 7, 2008: CounterCache para tener actualizado el número de elementos - PastelPHP
  3. Jan 7, 2008: Counting associated records - cakebaker
  4. Feb 11, 2008: CakePHP Tutorials :: PseudoCoder.com

Post a Comment