模型层通过获取器(get+字段驼峰+Attr)自动格式化读取值,修改器(set+字段驼峰+Attr)自动处理写入值,withAttr可临时覆盖获取逻辑,getData/getOrigin绕过获取器获取原始值。

你需要让数据库里的时间戳自动转成“2024-05-20”这种可读格式,又不想在每个控制器里重复写date();你存密码时想自动MD5,但用Db::table()->update()却完全不加密——这些不是靠手动补逻辑能解决的,而是模型层该干的事。
定义获取器:让读取自动格式化
在模型类中添加public方法,命名必须为get + 字段名驼峰化 + Attr,例如数据库字段是create_time,就写getCreateTimeAttr。
方法第一个参数$value是数据库取出的原始值(比如1716234567),第二个参数$data是整行数据数组,可用于跨字段计算(如拼接姓名)。
如果字段为空或为null,【必须显式判断并返回空值】,否则date()会触发PHP警告:if (empty($value)) return '';
立即学习“PHP免费学习笔记(深入)”;
获取器只在$user->create_time、$user->toArray()、$user->toJson()时触发,Db::name('user')->select()完全不走它。
定义修改器:让写入自动转换
方法名为set + 字段名驼峰化 + Attr,例如setPasswordAttr处理password字段。
修改器只在模型写入流程中生效:User::create()、$user->save()、$user->data($data, true)会触发;where()->update()和Db::table()->update()直接绕过模型层,【setAttr一个字都不会执行】。
返回值才是最终入库内容。想空密码时填当前时间戳,就写return $value ?: time();。
若字段需存JSON(如config),除了写setConfigAttr,还必须在模型中声明protected $type = ['config' => 'json'];,否则save()只会存字符串,读出来还是字符串。
动态覆盖:用withAttr临时替换获取逻辑
方法一:控制器中链式调用
User::withAttr('email', fn($v) => substr($v, 0, 3) . '***@' . explode('@', $v)[1])->select();
方法二:传入闭包处理状态映射
User::withAttr('status', function($v) { return ['0'=>'禁用','1'=>'启用'][$v] ?? '未知'; })->find(123);
注意:【withAttr只对本次查询链式调用有效】,User::find(1)->withAttr(...)不会生效;且它优先级高于模型内定义的getStatusAttr,有冲突时以withAttr为准。
绕过获取器拿原始值
第一步:用$user->getData('field_name')获取单个字段原始值
第二步:用$user->getData()获取整行未加工的原始数组
第三步:用$user->getOrigin('field_name')也可取原始值,但仅限TP6.1+版本
这三步都不触发任何获取器,适合导出、日志、调试等需要真实存储值的场景。



















