如何运用ThinkPHP模型获取器和修改器,实现ThinkPHP字段自动读写处理技巧?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1003个文字,预计阅读时间需要5分钟。
最常见的原因为字段名拼写错误。数据库字段是 `create_time`,模型中必须写成 `getCreateTimeAttr`,而不是 `getCreate_timeAttr`、`getCreatetimeAttr` 或其他形式。ThinkPHP 不会报错,而是静默跳过,导致代码修改无效。
另一个高发问题是类型转换冲突:protected $type = ['create_time' => 'datetime'] 和 getCreateTimeAttr 同时存在时,时间先被转成 Carbon 对象,再传给获取器;而 date('Y-m-d', $value) 传入对象会返回空字符串或警告。解决办法只有两个:
• 去掉 $type 声明,让原始时间戳直通获取器
• 或在获取器里判断类型:if ($value instanceof \Carbon\Carbon) { return $value->format('Y-m-d'); }
还要注意空值:数据库该字段为 NULL,$value 就是 null,直接 date() 会触发 warning。加一句 if (empty($value)) return ''; 更稳妥。
setPasswordAttr 存不进数据库的三个原因
修改器只在模型层写入流程中触发,绕过模型就完全失效。这三个场景最容易踩坑:
立即学习“PHP免费学习笔记(深入)”;
-
Db::name('user')->insert(['password' => '123'])—— 修改器不执行,密码明文入库 - 模型用了
$readonly = ['password'],哪怕写了setPasswordAttr,赋值也会被丢弃 - 字段没在
$field白名单里(比如漏写了'password'),save()时直接过滤掉
更隐蔽的是:如果同时配置了 $casts = ['password' => 'encrypted'],Laravel 风格的 cast 会和 ThinkPHP 修改器抢着处理,结果可能加密两次或完全跳过。ThinkPHP 官方不推荐混用 $casts 和修改器,优先用修改器。
别在修改器里抛异常:throw new Exception() 很可能被模型静默吞掉,save() 返回 false 却没提示,debug 时只能靠日志硬查。
WithAttr 动态获取器比模型级更灵活的时机
当你只需要某一次查询启用格式化,而不是全局生效,WithAttr 是唯一选择。比如导出 Excel 要带完整时间,但列表页只要日期:
$list = User::withAttr('create_time', function ($value) { return date('Y-m-d', $value); })->select(); $export = User::withAttr('create_time', function ($value) { return date('Y-m-d H:i:s', $value); })->select();
注意两点:
• WithAttr 优先级低于模型内定义的获取器,如果模型已写 getCreateTimeAttr,WithAttr 不会覆盖它
• 它只影响当前查询结果,不改变模型实例后续调用行为,比如 $user->create_time 还是走模型获取器
批量处理多个字段也支持:withAttr(['email', 'status'], fn($v) => strtoupper($v)),但别滥用——字段一多,可读性下降,不如收进模型。
JSON 字段读写失败,90% 是缺了 $type 声明
数据库字段是 JSON 类型,模型里只写 protected $json = ['config']; 是不够的。ThinkPHP 5.1+ 要求必须配对声明:protected $type = ['config' => 'json'];。漏掉 $type,save() 时存的是字符串 "{...}",toArray() 里还是字符串,根本不会自动 json_decode。
空值处理容易被忽略:数据库存 NULL,$value 就是 null,json_decode(null) 返回 null,前端拿到 undefined。安全写法是:
<pre class="brush:php;toolbar:false;">public function getConfigAttr($value) { if (null === $value || '' === $value) { return []; } return json_decode($value, true) ?: []; }
最后提醒:MySQL 5.7+ 的 JSON 字段,别试图用 'array' 替代 <code>'json' 类型,ThinkPHP 不识别,自动处理链直接断裂。
本文共计1003个文字,预计阅读时间需要5分钟。
最常见的原因为字段名拼写错误。数据库字段是 `create_time`,模型中必须写成 `getCreateTimeAttr`,而不是 `getCreate_timeAttr`、`getCreatetimeAttr` 或其他形式。ThinkPHP 不会报错,而是静默跳过,导致代码修改无效。
另一个高发问题是类型转换冲突:protected $type = ['create_time' => 'datetime'] 和 getCreateTimeAttr 同时存在时,时间先被转成 Carbon 对象,再传给获取器;而 date('Y-m-d', $value) 传入对象会返回空字符串或警告。解决办法只有两个:
• 去掉 $type 声明,让原始时间戳直通获取器
• 或在获取器里判断类型:if ($value instanceof \Carbon\Carbon) { return $value->format('Y-m-d'); }
还要注意空值:数据库该字段为 NULL,$value 就是 null,直接 date() 会触发 warning。加一句 if (empty($value)) return ''; 更稳妥。
setPasswordAttr 存不进数据库的三个原因
修改器只在模型层写入流程中触发,绕过模型就完全失效。这三个场景最容易踩坑:
立即学习“PHP免费学习笔记(深入)”;
-
Db::name('user')->insert(['password' => '123'])—— 修改器不执行,密码明文入库 - 模型用了
$readonly = ['password'],哪怕写了setPasswordAttr,赋值也会被丢弃 - 字段没在
$field白名单里(比如漏写了'password'),save()时直接过滤掉
更隐蔽的是:如果同时配置了 $casts = ['password' => 'encrypted'],Laravel 风格的 cast 会和 ThinkPHP 修改器抢着处理,结果可能加密两次或完全跳过。ThinkPHP 官方不推荐混用 $casts 和修改器,优先用修改器。
别在修改器里抛异常:throw new Exception() 很可能被模型静默吞掉,save() 返回 false 却没提示,debug 时只能靠日志硬查。
WithAttr 动态获取器比模型级更灵活的时机
当你只需要某一次查询启用格式化,而不是全局生效,WithAttr 是唯一选择。比如导出 Excel 要带完整时间,但列表页只要日期:
$list = User::withAttr('create_time', function ($value) { return date('Y-m-d', $value); })->select(); $export = User::withAttr('create_time', function ($value) { return date('Y-m-d H:i:s', $value); })->select();
注意两点:
• WithAttr 优先级低于模型内定义的获取器,如果模型已写 getCreateTimeAttr,WithAttr 不会覆盖它
• 它只影响当前查询结果,不改变模型实例后续调用行为,比如 $user->create_time 还是走模型获取器
批量处理多个字段也支持:withAttr(['email', 'status'], fn($v) => strtoupper($v)),但别滥用——字段一多,可读性下降,不如收进模型。
JSON 字段读写失败,90% 是缺了 $type 声明
数据库字段是 JSON 类型,模型里只写 protected $json = ['config']; 是不够的。ThinkPHP 5.1+ 要求必须配对声明:protected $type = ['config' => 'json'];。漏掉 $type,save() 时存的是字符串 "{...}",toArray() 里还是字符串,根本不会自动 json_decode。
空值处理容易被忽略:数据库存 NULL,$value 就是 null,json_decode(null) 返回 null,前端拿到 undefined。安全写法是:
<pre class="brush:php;toolbar:false;">public function getConfigAttr($value) { if (null === $value || '' === $value) { return []; } return json_decode($value, true) ?: []; }
最后提醒:MySQL 5.7+ 的 JSON 字段,别试图用 'array' 替代 <code>'json' 类型,ThinkPHP 不识别,自动处理链直接断裂。

