很多时候,Web 应用程序需要显示相关对象的计数。例如,在显示文章列表时,您可能想要显示每篇文章有多少评论。或者在显示用户时,您可能想要显示她有多少朋友/关注者。计数器缓存行为适用于这种情况。计数器缓存将在调用它时更新关联模型中分配的选项字段。这些字段应该存在于数据库中,并且类型为 INT。
您像其他行为一样启用计数器缓存行为,但它不会做任何事情,直到您配置一些关系和每个关系上应该存储的字段计数。使用下面的示例,我们可以使用以下方法为每篇文章缓存评论计数
class CommentsTable extends Table
{
public function initialize(array $config): void
{
$this->addBehavior('CounterCache', [
'Articles' => ['comment_count']
]);
}
}
注意
列 comment_count
应该存在于 articles
表中。
计数器缓存配置应该是一个关系名称和该关系的特定配置的映射。
如您所见,您需要在您实际想要更新字段的关联的“另一端”添加行为。在这个示例中,行为被添加到 CommentsTable
,即使它更新了 ArticlesTable
中的 comment_count
字段。
计数器的值将在每次保存或删除实体时更新。计数器不会在以下情况下更新
在不更改数据的情况下保存实体,或
使用 updateAll()
,或
使用 deleteAll()
,或
执行您编写的 SQL 代码
如果您需要为少于所有相关记录的记录保留缓存的计数器,您可以提供其他条件或查找器方法来生成计数器值
// Use a specific find method.
// In this case find(published)
$this->addBehavior('CounterCache', [
'Articles' => [
'comment_count' => [
'finder' => 'published',
],
],
]);
如果您没有自定义查找器方法,您可以提供一个条件数组来查找记录
$this->addBehavior('CounterCache', [
'Articles' => [
'comment_count' => [
'conditions' => ['Comments.spam' => false],
],
],
]);
如果您希望计数器缓存更新多个字段,例如同时显示条件计数和基本计数,您可以在数组中添加这些字段
$this->addBehavior('CounterCache', [
'Articles' => ['comment_count',
'published_comment_count' => [
'finder' => 'published',
],
],
]);
如果您希望自己计算计数器缓存字段值,您可以将 ignoreDirty
选项设置为 true
。这将防止在您之前将其设置为脏状态时重新计算该字段
$this->addBehavior('CounterCache', [
'Articles' => [
'comment_count' => [
'ignoreDirty' => true,
],
],
]);
最后,如果自定义查找器和条件不适合,您可以提供一个回调函数。您的函数必须返回要存储的计数值
$this->addBehavior('CounterCache', [
'Articles' => [
'rating_avg' => function ($event, $entity, $table, $original) {
return 4.5;
}
],
]);
您的函数可以返回 false
以跳过更新计数器列,或者返回一个生成计数值的 SelectQuery
对象。如果您返回一个 SelectQuery
对象,您的查询将用作更新语句中的子查询。 $table
参数是指向包含行为的表对象的(而不是目标关系的),以方便起见。回调至少调用一次,其中 $original
设置为 false
。如果实体更新更改了关联,则回调将调用第二次,其中 true
,然后返回值将更新先前关联项目的计数器。
注意
计数器缓存行为仅适用于 belongsTo
关联。例如,对于“评论属于文章”,您需要将计数器缓存行为添加到 CommentsTable
以生成文章表的 comment_count
。
版本 5.1.2 中更改: 从 CakePHP 5.1.2 开始,计数器缓存值使用单个查询(使用子查询)来更新,而不是使用单独的查询来获取计数和更新记录。如果需要,您可以通过在配置中将 useSubQuery 键设置为 false 来禁用子查询的使用 [‘Articles’ => [‘comment_count’ => [‘useSubQuery’ => false]]
可以在 belongsToMany
关联中使用计数器缓存行为。首先,您需要将 through
和 cascadeCallbacks
选项添加到 belongsToMany
关联
'through' => 'CommentsArticles',
'cascadeCallbacks' => true
另请参阅 使用“through”选项 如何配置自定义联接表。
CommentsArticles
是联接表类名。如果您没有它,您应该使用 bake CLI 工具创建它。
然后,在 src/Model/Table/CommentsArticlesTable.php
中,您需要使用与上面描述相同的代码添加行为。
$this->addBehavior('CounterCache', [
'Articles' => ['comments_count'],
]);
最后,使用 bin/cake cache clear_all
清除所有缓存,然后试用。