thinkphp 的 getoriginal() 仅保存首次查询时的字段快照,不随后续修改更新,且受获取器、软删除、json 字段等影响易失真;可靠比对须显式查库 + getdata()。

直接结论:ThinkPHP 的“原始数据”不是随时可取的快照,而是依赖查询时机和模型状态的瞬时值;getOriginal() 只反映上次 select() 或 find() 后缓存的旧值,中间若被其他逻辑改过(比如触发了获取器、自动写入、事件修改),就失真了。
为什么 getOriginal() 经常拿不到真实旧值
它只保存模型实例从数据库查出来那一刻的字段快照,后续所有 save()、update()、甚至手动赋值(如 $model->name = 'xxx')都不会自动刷新 origin。更关键的是:一旦模型启用了获取器(accessor),$model->name 返回的是处理后的值,而 getOriginal('name') 才是数据库原始值——但前提是这个原始值还没被覆盖。
常见错误现象:
- 在
beforeUpdate里调用$this->getOriginal(),结果和当前待更新数据几乎一样(因为上一次save()已把origin覆盖为旧的“新值”) - 使用
Db::name('user')->where(...)->update()直接走查询构造器,模型事件不触发,origin根本没机会加载 - 批量操作
saveAll()后,每个模型的origin已被设为本次保存后的数据,无法回溯保存前的状态
真正可靠的方式:显式查库拿真实旧值
想确保比对的是数据库里此刻的真实旧值,必须绕过模型缓存,主动发起一次查询。这不是过度设计,而是 ThinkPHP 模型层不维护事务级一致性快照的必然选择。
实操建议:
- 在模型的
beforeUpdate钩子中,用Db::name($this->getTable())->where('id', $this->id)->find()查出当前数据库记录 - 别用
$this->find(),它可能走模型缓存或触发获取器,干扰原始字段值 - 查出来的数组直接用
getData()提取原始字段(排除获取器影响),例如:$oldData = (new static())->where('id', $this->id)->find()->getData(); - 如果主键不是
id,需动态获取:$pk = $this->getPk(); $where = [$pk => $this->$pk];
getData() 和 getOriginal() 的适用场景差异
getData() 返回当前模型实例中所有已赋值字段(含刚 set 进去但未保存的),不经过获取器;getOriginal() 返回的是模型初始化时从数据库捞出并缓存的那版原始值——两者语义完全不同。
使用场景判断:
- 做字段变更比对(如日志记录):优先用显式查库 +
getData(),最稳 - 调试时快速看模型当前持有哪些字段值:用
$model->getData() - 确认某字段是否被获取器转换过:对比
$model->field和$model->getData('field') - 想还原“刚查出来时的样子”,且确定没被改过:可用
getOriginal(),但要加注释说明这个假设
容易被忽略的兼容性细节
ThinkPHP 6.x 中,getOriginal() 在软删除模型里行为有隐式陷阱:delete_time 字段为 null 时,PHP 的松散比较(==)会把它当成空字符串,导致差异识别失败。必须用严格比较 ===,且建议统一用 json_encode($val, JSON_UNESCAPED_UNICODE) 序列化后再比对,避免中文、浮点数、null/'' 混淆。
另外,JSON 类型字段在数据库里存的是字符串,但模型取出后可能是数组或对象——getData() 返回的是反序列化后的结构,getOriginal() 则返回原始字符串。若需比对内容而非格式,务必先统一序列化。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











