ThinkPHP 的 getAttr 和 setAttr 是模型自动触发的约定方法,需严格按命名规范(get/set+驼峰字段名+Attr)定义,否则静默失效;获取器仅在读取数据后触发,修改器仅在模型写入流程中生效,WithAttr 可动态覆盖获取器逻辑。

ThinkPHP 的 getAttr 和 setAttr 不是手动调用的函数,而是模型自动触发的约定方法;你写对命名、放对位置,框架就会在读/写时悄悄帮你处理字段——但写错一个字母、漏掉一个 Attr 后缀,它就完全不生效,还不报错。
获取器 getFieldNameAttr 怎么写才有效
获取器只在「读取数据后」触发,比如 $user->status、$user->toArray()、$user->toJson()。它不是万能钩子,不会在原始 SQL 查询或 Db::table() 中运行。
- 方法名必须是
get+ 字段名驼峰化 +Attr,例如数据库字段user_status→ 方法名getUserStatusAttr - 第一个参数
$value是该字段从数据库取出的原始值(可能是null);第二个可选参数$data是整行数据数组,可用于依赖其他字段的逻辑(比如getFullnameAttr拼接$data['first_name']和$data['last_name']) - 如果字段不存在(如虚拟字段
getAgeAttr),$value通常无意义,直接忽略,靠$data计算即可 - 想绕过获取器拿原始值?用
$user->getData('status')或$user->getData()
修改器 setFieldNameAttr 什么情况下会失效
修改器只在「模型写入流程」中触发,where()->update()、Db::name()->update() 这类原生更新完全跳过模型层,setAttr 一个字都不会执行。
- 有效触发场景:
$user->save()、User::create([...])、$user->data([...], true)、$user->appendData([...], true) - 方法签名同获取器:
set+ 驼峰字段名 +Attr,例如setPasswordAttr处理password字段 - 参数
$value是你赋的值(比如$user->password = '123456'),返回值才是最终入库内容;若想设默认值(如空时填当前时间),写成return $value ?: date('Y-m-d H:i:s'); - 注意:TP6 不再支持
auto数组或_validate,老教程里那些配置项已彻底移除,写了也白写
WithAttr 动态获取器优先级比模型定义高
控制器里用 WithAttr 可以临时覆盖模型中已有的获取器逻辑,适合同一字段在不同接口需要不同格式的场景(比如管理后台要完整邮箱,APP 接口要掩码显示)。
立即学习“PHP免费学习笔记(深入)”;
User::withAttr('email', fn($v) => substr($v, 0, 3) . '***@' . explode('@', $v)[1])->select()- 它只影响本次查询结果,不影响模型本身;且仅对查询链式调用有效,
User::find(1)后再调withAttr无效 - 如果模型没定义
getEmailAttr,withAttr依然生效;但如果两者都存在,withAttr的回调一定优先生效
最易被忽略的一点:字段名和方法名的驼峰转换必须严格匹配数据库实际字段,比如 create_time → getCreateTimeAttr,少个 C 或多下划线都会静默失败;调试时别只看逻辑,先核对方法名拼写和大小写。



















