CakePHP, Using CounterCache to keep track of Comments

31st May, 2008 – 7:59 pm

CounterCache is one of the new Core Model Behaviors in CakePHP 1.2 and it’s an absolute life saver for working with hasMany relationships where you want to OrderBy one of the children.

The basic idea behind a CounterCache is to keep tabs on how ‘children’ a specific model has, this is best explained in a quick snippet of CakePHP. In the following example I am going to set up a simple relationship of Users and Comments, however, I want to add the condition that posts must be moderated first as indicated by a `moderated` Boolean field in the MySQL table.

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `comments_count` int(11) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `message` text NOT NULL,
  `moderated` tinyint(1) NOT NULL DEFAULT ‘0′,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 

Users Model: /app/models/user.php

<?php
Class User extends AppModel {
        var $name = ‘User’;
        var $hasMany = array(‘Comment’);
}
?>
 

Comments Model: /app/models/comment.php

<?php
Class Comment extends AppModel {
        var $name = ‘Comment’;
        var $actsAs = array(‘CounterCache’);
        var $belongsTo = array (‘User’ => array (
                ‘foreignKey’ => ‘user_id’,
                ‘counterCache’ => ‘comments_count’,
                ‘counterScope’ => ‘moderated = 1′)
        );
}
?>
 

So the magic being introduced here is the CounterCache Behavior, in the Comments Model (which is included by setting is in the $actsAs variable). The ‘counterCache’ => ‘comments_count’ value indicates to column on the `belongsTo` table which will contain the count and the ‘counterScope’ => ‘moderated = 1′ tells CounterCache to only increment the count value when this SQL Condition is met (in this case, `moderated` must be true).

Go ahead, build the application, throw some scaffolding into the Controllers and watch in awe as the `comments_count` value increments each time you save a comment where $data['Comment']['moderated'] = 1. Now, this is all well and good, but in the real world, users won’t be setting the moderated value, instead you will have a Controller action to approve comments which looks something like this:

<?php
Class Comment extends AppModel {
        var $name = ‘Comment’;
        var $actsAs = array(‘CounterCache’);
        var $belongsTo = array (‘User’ => array (
                ‘foreignKey’ => ‘user_id’,
                ‘counterCache’ => ‘comments_count’,
                ‘counterScope’ => ‘moderated = 1′)
        );

        /**
         * Callback for when Cake has performed a save() operation.
         *
         * @param Boolean $created TRUE if save() performed a CREATE
         * @return Void
        */

        function afterSave($created) {
                if (!$created) {
                        // Kick the counterCache value.
                        $this->updateCounterCache();
                }
        }
}
?>
 

You could further tweak this callback to enhance performance, only updating the counterCache when logic dictates it necessary.

Post a Comment