CounterCache

class Cake\ORM\Behavior\CounterCacheBehavior

Often times web applications need to display counts of related objects. For example, when showing a list of articles you may want to display how many comments it has. Or when showing a user you might want to show how many friends/followers she has. The CounterCache behavior is intended for these situations. CounterCache will update a field in the associated models assigned in the options when it is invoked. The fields should exist in the database and be of the type INT.

Basic Usage

You enable the CounterCache behavior like any other behavior, but it won’t do anything until you configure some relations and the field counts that should be stored on each of them. Using our example below, we could cache the comment count for each article with the following:

class CommentsTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('CounterCache', [
            'Articles' => ['comment_count']
        ]);
    }
}

The CounterCache configuration should be a map of relation names and the specific configuration for that relation.

The counter’s value will be updated each time an entity is saved or deleted. The counter will not be updated when you use updateAll() or deleteAll(), or execute SQL you have written.

Advanced Usage

If you need to keep a cached counter for less than all of the related records, you can supply additional conditions or finder methods to generate a counter value:

// Use a specific find method.
// In this case find(published)
$this->addBehavior('CounterCache', [
    'Articles' => [
        'comment_count' => [
            'finder' => 'published'
        ]
    ]
]);

If you don’t have a custom finder method you can provide an array of conditions to find records instead:

$this->addBehavior('CounterCache', [
    'Articles' => [
        'comment_count' => [
            'conditions' => ['Comments.spam' => false]
        ]
    ]
]);

If you want CounterCache to update multiple fields, for example both showing a conditional count and a basic count you can add these fields in the array:

$this->addBehavior('CounterCache', [
    'Articles' => ['comment_count',
        'published_comment_count' => [
            'finder' => 'published'
        ]
    ]
]);

If you want to calculate the CounterCache field value on your own, you can set the ignoreDirty option to true. This will prevent the field from being recalculated if you’ve set it dirty before:

$this->addBehavior('CounterCache', [
    'Articles' => [
        'comment_count' => [
            'ignoreDirty' => true
        ]
    ]
]);

Lastly, if a custom finder and conditions are not suitable you can provide a callback function. Your function must return the count value to be stored:

$this->addBehavior('CounterCache', [
    'Articles' => [
        'rating_avg' => function ($event, $entity, $table, $original) {
            return 4.5;
        }
    ]
]);

Your function can return false to skip updating the counter column, or a Query object that produced the count value. If you return a Query object, your query will be used as a subquery in the update statement. The $table parameter refers to the table object holding the behavior (not the target relation) for convenience. The callback is invoked at least once with $original set to false. If the entity-update changes the association then the callback is invoked a second time with true, the return value then updates the counter of the previously associated item.

Note

The CounterCache behavior works for belongsTo associations only. For example for “Comments belongsTo Articles”, you need to add the CounterCache behavior to the CommentsTable in order to generate comment_count for Articles table.

It is possible though to make this work for belongsToMany associations. You need to enable the CounterCache behavior in a custom through table configured in association options. See how to configure a custom join table Using the ‘through’ Option.

Changed in version 3.6.0: Returning false to skip updates was added.