ThinkPHP模型自动填充加密字段在save()中不生效,根本原因是自动填充仅在create()或验证时触发,而save()直接写入不执行填充;TP6推荐用setPasswordAttr修改器,并避免重复加密、继承覆盖及升级兼容问题。

ThinkPHP 模型自动填充加密字段用 save() 时没生效
根本原因是 ThinkPHP 的自动填充(auto 或 filter)默认只在 create() 或表单验证阶段触发,而 save() 直接写入时不会重新走填充逻辑。尤其当你用 $model->data([...])->save() 或批量更新时,加密字段很容易被原样存进去。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 改用
allowField(true)+data()+save()组合,并在data()前手动加密关键字段,比如:$data = ['username' => 'admin', 'password' => '123456']; $data['password'] = md5($data['password']); $model->data($data)->save();
- 如果坚持用自动填充,必须确保调用的是
create()(TP5.0/5.1)或validate()->save()(TP6),且填充规则定义在模型的protected $auto/$filter中,例如:protected $filter = [ 'password' => 'md5', ]; - TP6 中更推荐用「修改器」(mutator)替代传统填充:在模型里定义
setPasswordAttr($value)方法,它会在赋值和保存前自动触发,比$filter更可控、更易调试。
TP6 模型事件中加密密码导致重复哈希
常见错误是把加密逻辑同时写在 setPasswordAttr 和 beforeWrite 事件里,或者在事件里对已加密的值再次调用 md5() —— 结果就是数据库里存的是 md5(md5('123456')),登录永远失败。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 只在一个地方做加密:优先用修改器
setPasswordAttr,它天然区分「原始输入」和「待存值」;beforeWrite适合处理非字段级逻辑(如日志、关联更新)。 - 在修改器里加判断,避免二次加密:
public function setPasswordAttr($value) { if (strlen($value) === 32 && ctype_xdigit($value)) { return $value; // 已是 md5,不重算 } return md5($value); } - 调试时打日志确认触发次数:
trace('password attr triggered: ' . $value);,TP6 默认日志在runtime/log/下,别依赖dump()看不到。
扩展模型类时加密逻辑被父类覆盖
自定义基类(如 BaseModel)里写了 setPasswordAttr,子模型(如 UserModel)也写了同名方法,但没调用 parent::setPasswordAttr($value),结果父类的通用加密逻辑完全失效。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 基类中不要直接写具体算法,而是留钩子:
protected $passwordHashMethod = 'md5'; public function setPasswordAttr($value) { return call_user_func($this->passwordHashMethod, $value); } - 子模型按需覆盖属性:
class UserModel extends BaseModel { protected $passwordHashMethod = 'sha256'; } - 若需完全重写,务必显式调用父类并做条件合并,比如先校验再加密,而不是直接替换。
兼容性陷阱:TP5.1 升级 TP6 后加密字段变空
TP6 废弃了 $auto 数组填充,改用修改器 + 事件机制;很多老项目升级后,模型里还留着 protected $auto = ['password' => 'md5'],但这个配置在 TP6 完全不解析,字段就以明文入库了。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 升级前全局搜索
$auto、$filter、$type,逐个替换成对应修改器方法。 - TP6 的
save()默认只写入允许字段(allowField),如果加密字段不在$schema或没显式allowField(['password']),即使你设置了修改器,它也不会被写入 —— 这个细节容易漏查。 - 用
php think optimize:schema生成字段映射后,检查生成的$schema是否包含加密字段,否则修改器形同虚设。


















