If you have a hasMany association, a common use case is that you want to list records of one table plus the number of associated records. For example you want to list the ten latest posts together with the number of comments each post has.

There are different ways how you can implement this. Probably the most common approaches are to use a counter cache or to use a sub query.

A counter cache is simply an additional counter field in the posts table (if we stay with the example of Post hasMany Comments). If a comment is added to a post, the counter is incremented. And it is decremented, if a comment is removed. A simple concept. You can find tutorials about using this concept with CakePHP on the blog of Robert Beekman (using the counter_cache behavior from the bakery) and on the blog of Christian Winther (using the built-in counter cache functionality from CakePHP 1.2beta).

The second approach is to use a sub query. It is quite straight-forward, the only “difficulty” is to bring the result into the format used by CakePHP. Here the code:

// in the Post model
function findAllAndCountComments($conditions) {
    $this->recursive = 0;
    $posts = $this->findAll($conditions, array('Post.*', "(select count(c.id) from comments as c where c.post_id = Post.id) as 'comment_count'"));
    $posts = $this->toCakeArrayFormat($posts);

    return $posts;
}

private function toCakeArrayFormat($posts) {
    $result = array();

    for ($i = 0; $i < sizeof($posts); $i++) {
        $result[$i]['Post'] = $posts[$i]['Post'];
        $result[$i]['Post']['comment_count'] = $posts[$i][0]['comment_count'];
    }

     return $result;
}

From a performance point of view this approach is not as good as the counter cache approach. It requires n+1 selects on the database side (maybe it gets internally optimized, I don’t know) whereas with a counter cache only one select is needed. On the other hand it doesn’t depend on some “magic” functionality. But in the end it depends on your preferences which approach you will use.