You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CakePHP在isEmpty判断条件下查询缓存不生效问题

问题原因

核心问题出在CakePHP ORM的惰性执行机制和方法调用顺序错误:

  • 调用$categoriesTable->find()时不会立刻执行SQL查询,仅返回查询构造器对象,只有当你调用isEmpty()、all()、first()或者遍历查询对象时,才会真正发起数据库请求。
  • 查询对象的cache()配置,只有在查询执行前挂载才会生效:查询执行时会检测是否配置了缓存规则,有则在拿到结果后写入缓存;如果查询已经执行完成,后续再调用cache()不会触发任何缓存写入操作。

你当前的代码逻辑顺序完全颠倒:先调用$categories->isEmpty()触发查询执行(此时还没配置缓存,执行完不会写Redis),之后才调用->cache()挂载配置,此时查询已经跑完,缓存配置根本不生效,所以你紧接着读缓存永远是null,每次请求都会进入缓存未命中的分支,重复输出hello。

你观察到的另外两个现象也完全符合这个逻辑:

  • 注释掉isEmpty()判断后,查询对象不会提前执行,等到后续视图渲染触发查询时,你已经完成了缓存配置挂载,所以查询执行时会正常写入缓存,功能正常。
  • 把判断改成$categoriesTable->find('all')->isEmpty()时,isEmpty()是在一个全新的独立查询对象上调用的,你原来赋值给$categories的查询对象没有被提前触发,等到后续使用时才执行,此时已经挂载了缓存配置,所以能正常写缓存。但这种写法会额外多执行一条完全相同的SQL,属于无意义的性能损耗。

另外原有逻辑还存在一个隐患:缓存未命中时传给视图的是查询对象,缓存命中时传给视图的是缓存里存储的结果集,两种数据类型不一致,很容易触发序列化或渲染错误。

修正代码

调整逻辑顺序:先给查询挂载缓存配置,再触发查询执行,最后判断结果是否为空,如果为空就主动删除缓存,保证只有非空结果才会被缓存,同时统一返回给视图的数据类型:

public function getCategories()
{   
    $categoriesTable = TableRegistry::getTableLocator()->get('Categories');
    $cachedCategories = \Cake\Cache\Cache::read('categories', 'redis_cache');

    if ($cachedCategories === null) {
        // 先构造查询,提前挂载缓存配置
        $categoriesQuery = $categoriesTable->find();
        $categoriesQuery->cache('categories', 'redis_cache');
        // 触发查询执行,拿到实际结果集
        $categories = $categoriesQuery->all();

        // 结果为空时删除缓存,避免空值被存入Redis
        if ($categories->isEmpty()) {
            \Cake\Cache\Cache::delete('categories', 'redis_cache');
        }

        $this->set('categories', $categories);
    } else {
        $this->set('categories', $cachedCategories);
    }

    $this->viewBuilder()->setOption('serialize', ['categories']); 
}

内容的提问来源于stack exchange,提问作者Abu Sayed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:09:42