获取器和修改器是模型层数据进出的强制过滤网,用于自动处理时间戳、状态码、密码加解密等,仅在模型上下文生效;常见失效原因包括驼峰命名错误、类型转换冲突及null未判空。

获取器和修改器不是锦上添花的语法糖,而是模型层数据进出的强制过滤网——它让时间戳自动变日期、状态码转中文、密码写入前加密、读出来自动解密,且只在模型上下文生效,绕过模型就完全不触发。
getCreateTimeAttr 为什么总不生效?
最常见原因就两个:驼峰写错、类型转换冲突。
-
create_time必须写成getCreateTimeAttr,写成getCreate_timeAttr或getCreatetimeAttr都静默失效,框架不报错也不提醒 -
protected $type = ['create_time' => 'datetime']和getCreateTimeAttr同时存在时,$value传进来的是Carbon对象,不是时间戳,date('Y-m-d', $value)直接返回空字符串或 warning - 数据库该字段为
NULL时,$value是null,没判空直接date()会触发 PHP warning
稳妥写法:
public function getCreateTimeAttr($value)
{
if (empty($value)) {
return '';
}
if ($value instanceof \Carbon\Carbon) {
return $value->format('Y-m-d');
}
return date('Y-m-d', $value);
}
setPasswordAttr 存不进数据库的三个典型场景
修改器只在模型写入流程中起作用,不是“赋值就触发”。以下操作完全跳过它:
-
Db::name('user')->insert(['password' => '123'])—— 原生插入,setPasswordAttr一概不执行 - 字段被设为只读:
protected $readonly = ['password'],哪怕写了修改器,赋值也会被丢弃 - 字段不在白名单里:
protected $field = ['id', 'name']漏了'password',save()时直接过滤掉
另外注意:$user->data(['password' => '123'], true) 的第二个参数 true 必须显式传入,否则修改器不触发。
WithAttr 动态覆盖获取器的实际用法
当同一字段在不同接口需要不同格式(比如管理后台显示完整邮箱,APP 接口要掩码),withAttr 是唯一能临时覆盖模型级获取器的方式。
-
withAttr只对链式查询有效:User::withAttr('email', fn($v) => substr($v, 0, 3) . '***@' . explode('@', $v)[1])->select() -
User::find(1)->withAttr(...)这种写法无效,withAttr必须在查询构造阶段调用 - 如果模型已定义
getEmailAttr,withAttr的回调仍会优先生效;但若模型没定义,withAttr照样可用
别指望它影响 toArray() 之后的数据——它只改查询结果对象的属性访问行为,不改原始数据结构。
加密字段必须用修改器+hidden,不能靠数据库函数
用 AES_ENCRYPT() 之类 SQL 层加解密,会导致 ORM 失效:查 where('password', '123') 实际比对的是明文,而库里存的是密文,永远不匹配。
- 加密必须可逆(如
openssl_encrypt),不能用md5或password_hash -
setPasswordAttr里要判空、防重复加密(比如先检查是否已是 base64 密文) -
hidden = ['password']是必须项,否则toJson()或 API 返回时可能意外暴露密文 -
getData('password')能拿到原始密文,$user->password才走getPasswordAttr解密
真正容易被忽略的是:CLI 脚本、队列任务、关联模型加载(如 User::with('profile'))都依赖这套机制——一旦漏掉修改器或 hidden,轻则数据错乱,重则密钥泄露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











