thinkphp无内置diff工具,需用getdata()取原始值、严格比较+json_encode统一序列化、排除系统字段、手动逐字段比对并转义特殊值。

如何用 ThinkPHP 记录并比对两条数据的字段差异
直接结论:ThinkPHP 本身不提供内置的数据 diff 工具,必须手动提取字段值、做数组对比。关键在「取值时机」和「字段过滤」——别直接比 $model->toArray(),它会带入时间戳、JSON 字段原始字符串、空字符串与 null 的语义混淆。
实操建议:
- 用
$model->getData()获取原始赋值数据(不含自动写入字段),再用$model->getChangedData()确认哪些字段真被改过 - 对需要比对的字段,统一用
json_encode($val, JSON_UNESCAPED_UNICODE)序列化后再比较,避免中文编码或浮点精度导致误判 - 排除系统字段:
['id', 'create_time', 'update_time', 'delete_time'],这些不该计入业务差异 - 注意软删除字段(如
delete_time)为null时,PHP 中null == ''为 true,需用严格比较===
ThinkPHP 6.x 中实现历史版本对比的最小可行方案
最轻量的做法不是建完整版本表,而是每次更新前先查旧数据快照,存到一个临时结构里再比对。ThinkPHP 6 的事件机制刚好能卡在更新前介入。
实操建议:
- 在模型中定义
protected static $observe = ['VersionObserver'];,监听before_update事件 - 在观察者里用
$model->where('id', $model->id)->find()查出旧记录,调用$old->getData()获取原始值 - 比对逻辑封装成方法:
diffFields($oldData, $newData, ['title', 'content', 'status']),只比业务关心字段 - 避免在
before_update中修改$model属性,否则会影响后续 save 行为;差异结果应写入日志或独立变更记录表
JSON 字段、关联属性、自动转换字段怎么安全比对
ThinkPHP 对 json 类型字段默认用 json_decode 转成数组,但 toArray() 又可能把它转回字符串;关联模型默认不加载,with() 后又容易嵌套太深。这些都会让 diff 失效。
实操建议:
- JSON 字段(如
extra_info)务必用json_encode($old->extra_info, JSON_UNESCAPED_UNICODE | JSON_SORT_KEYS),加JSON_SORT_KEYS避免键顺序不同被判为差异 - 关联字段(如
category.name)不要依赖with()后的属性访问,应显式查出并比对 ID 或关键字段,例如$old->category_id !== $new->category_id - 有自动转换的字段(如
status是枚举,模型里定义了getStatusLabelAttr),比对时一定要用原始值$old->status,而非$old->status_label - 时间字段统一转为 Unix 时间戳再比:
strtotime($old->publish_time) !== strtotime($new->publish_time),避开格式/时区干扰
为什么不能直接用 array_diff_assoc 做全文档比对
因为 array_diff_assoc 会把 null、''、0、false 全部视为等价,且不处理嵌套数组。ThinkPHP 模型输出的数组里常混着这几种“空值”,尤其在未设置默认值的字段上。
实操建议:
- 手写比对循环,用
foreach ($fields as $field)逐个判断:!isset($old[$field]) && !isset($new[$field])→ 跳过;!isset($old[$field]) xor !isset($new[$field])→ 标记为新增/删除;其余走严格比较 - 对数值字段,用
is_numeric()+(float)强转再比,避免'1'和1判为不同 - 对布尔字段,统一转成
(bool)再比,ThinkPHP 从数据库读出的 tinyint(1) 可能是 int 0/1,也可能是 string "0"/"1" - 真正要展示给用户的差异,必须附带字段中文名映射,比如
['title' => '标题', 'content' => '正文'],否则运维看不懂content改了哪一行
字段级差异看着简单,实际最难的是“什么算变化”——空格增删、换行符 CRLF/LF 不一致、HTML 实体编码、富文本前后空格,都可能让 diff 结果失真。业务越重,越要提前约定字段清洗规则,而不是指望一次比对包打天下。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










