tp8中cache()方法不再支持闭包参数,必须显式传入字符串键;默认启用查询指纹缓存,但含where闭包时需手动构造可区分键,否则缓存失效。

TP6 升级到 TP8 后,查询缓存(cache())不再支持直接传入闭包作为缓存键生成逻辑,原有写法会静默失效或抛出异常——这不是配置问题,而是底层缓存键生成机制重构导致的兼容断层。
TP8 中 cache() 不再接受闭包参数
TP6 允许这样写:->cache(function ($query) { return md5($query->getRawSql()); }),TP8 会直接报错 TypeError: cache() expects parameter 1 to be string or int, Closure given。因为 TP8 的 cache() 方法签名已收紧为 cache(?string $key = null, ?int $expire = null, ?string $tag = null),闭包被彻底移除。
- 闭包曾用于动态生成 SQL 相关缓存键,但 TP8 改用更稳定的「查询指纹」机制(基于表名、字段、where 条件结构哈希),不再鼓励运行时拼 SQL
- 若强行保留旧逻辑,需手动提取条件并构造键,比如把
['status', '=', $status]转成"user_status_{$status}" - TP8 默认对
select()/find()等终态方法自动启用轻量缓存指纹,无需显式调用cache()也能命中(前提是开启全局查询缓存)
where 条件含闭包时,缓存键无法自动推导
当查询中用了 where(function ($query) { ... }),TP8 的默认指纹算法会跳过闭包内部逻辑,仅记录「存在闭包」这一事实,导致不同条件的闭包查询共用同一个缓存键(例如两个不同 $uid 的子查询都命中 user_subquery 键)。
- 必须显式传入可区分的
$key,例如:->cache("user_active_{$uid}_{$type}") - 避免用
serialize($where)或md5(json_encode($where))—— 闭包无法序列化,会 crash - 若条件来自请求参数,优先用参数组合(如
"user_list_status_{$status}_page_{$page}"),而非试图还原闭包语义
升级后缓存失效的典型表现与验证方式
上线后发现缓存未生效或频繁击穿,先检查日志中是否出现 Cache key not generated for query with closure 类提示(TP8.1+ 开启 debug 模式可见),或观察 Redis 中缓存 key 是否全为固定前缀 + 随机字符串(说明指纹失败,回退到了默认随机键)。
- 用
Db::listen()打印实际执行的 SQL 和缓存键:Db::listen(function ($sql, $time, $explain) { dump($sql, cache_key_used); }); - TP8.0 默认不缓存含
limit/order的分页查询,需手动加cache()并指定键 - 模型查询(
UserModel::where(...)->select())和 Db 查询(Db::name('user')->where(...)->select())的缓存指纹生成逻辑不一致,混用时注意键隔离
真正麻烦的不是闭包本身,而是开发者习惯把业务逻辑(比如权限过滤)塞进 where 闭包,又指望缓存自动理解它——TP8 把这个幻想砍掉了。缓存键必须由你明确声明,且不能依赖运行时不可控的结构。











