ThinkPHP怎么使用模型字段只读虚拟字段缓存穿透防护_ThinkPHP空结果也缓存策略【教程】

梦明小哥_1575

梦明小哥_1575

2026-04-08

516人浏览

原创

thinkphp中字段只读需在beforewrite钩子unset;虚拟字段靠getattr访问器+append声明+beforewrite过滤;缓存穿透须存{'__empty__':true}标记;空结果缓存与字段控制必须分层隔离,不可混写。

thinkphp怎么使用模型字段只读虚拟字段缓存穿透防护_thinkphp空结果也缓存策略【教程】

ThinkPHP 的模型字段只读、虚拟字段、缓存穿透防护、空结果缓存——这四个需求不能靠单一配置或一个钩子解决,必须分层处理:字段控制在模型层,缓存逻辑在查询层,穿透防护在缓存层,空值策略在业务判断层。

怎么让模型字段真正只读(不被 save() 修改)

ThinkPHP 没有 readonly 字段属性,protected $readonly 只影响 create()(已废弃),对 save() 无效。实际生效的方式是拦截写入逻辑:

  • 重写模型的 data() 方法,在设置数据时过滤掉只读字段(如 created_at、version)
  • 更稳妥的是在 beforeWrite 钩子中 unset:
    protected static function boot()
    {
        parent::boot();
        static::beforeWrite(function ($model) {
            unset($model['updated_at'], $model['status_lock']);
        });
    }
  • 注意:不要依赖数据库字段的 DEFAULT 或 GENERATED,TP 不会自动跳过这些字段的写入

怎么定义和使用虚拟字段(不映射到数据库)

虚拟字段不是靠 $schema 或 $type 声明的,而是靠手动注入 + 屏蔽持久化:

  • 在模型中定义访问器(getXXXAttr)并返回计算值,例如:getFullnameAttr 拼接 first_name 和 last_name
  • 必须同时在 $append 中声明,否则 toArray() 不包含该字段
  • 关键:在 beforeWrite 钩子里 unset 对应的虚拟字段名(如 fullname),否则它会被当成真实字段塞进 SQL
  • 若用 withAttr 定义转换,它只是输出格式化,不解决“是否写入”问题

缓存穿透防护:空结果到底该不该存?怎么存?

TP 自带的 cache() 方法默认不缓存 null 或空数组,直接导致穿透。必须显式控制:

  • 查不到时,用 Cache::set($key, ['__empty__' => true], $ttl) 存一个标记,避免反复查库
  • 查询逻辑要统一包装:先 Cache::get($key),命中后检查是否含 __empty__ 键,是则直接返回 null
  • 不要用 0、false、'' 作空标记——它们可能是合法业务值;用结构化标记(如数组键)才可靠
  • TP6.1+ 的 think-orm 支持 withCache() 的 empty 参数,但仅限于主键查询,关联查询仍需手写

为什么「缓存穿透防护」和「字段只读」容易串在一起出错

很多人把字段控制和缓存逻辑混在同一个方法里,比如在 getById() 中既做 unset 又做 Cache::get/set,结果出现两个典型问题:

  • 缓存中存了带虚拟字段的对象,下次取出时字段还在,但数据库其实没这列——因为没在 beforeWrite 清理,导致误更新
  • 空结果缓存用了 Cache::remember(),但它内部对 null 直接跳过存储,你以为防穿透了,其实没生效
  • 最隐蔽的坑:模型开启 auto_write_timestamp 后,save() 会强制写入 update_time,哪怕你前面 unset 过——得关掉或改用 updateTime 钩子手动控制

字段逻辑和缓存逻辑必须隔离:字段由模型生命周期钩子管,缓存由服务类或 Repository 封装,别让一个方法承担两层职责。

php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4023

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

851

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1049

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5881

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2803

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5860

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7801

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1050

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

932

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习