
ThinkPHP 的 Collection 不能直接用 diff() 比字段值
很多人试过 $collection1->diff($collection2),发现结果不对——它比的是整个对象引用或序列化后的字符串,不是按字段逐个对比。ThinkPHP 的 Collection 继承自 Laravel 的集合类,但没重写 diff() 的行为,底层走的是 === 或 == 判断,对模型实例基本无效。
实操建议:
- 别依赖
diff()、diffKeys()这类原生方法做记录级差异 - 把两条记录转成关联数组再比,比如用
$model->toArray()或$model->getAttributes() - 注意时间字段(如
create_time)可能带毫秒或时区偏移,直接比会误判,建议统一转为时间戳或截断到秒
手动对比两个模型实例的字段差异(推荐方案)
最稳的方式是自己遍历字段名,逐个比值。ThinkPHP 模型有 getSchemaFields()(TP6.1+)或可从 $model->schema 拿字段定义,但更简单的是直接用白名单或 getFillable() 避免比到内部属性。
常见错误现象:比出一堆 __data、__meta、exists 差异——这些是模型元信息,不是业务数据。
实操建议:
- 用
$model->getOriginal()获取原始数据库值(避免脏数据干扰) - 过滤掉以
_开头的字段(如_sql、_query)和布尔标记(exists、wasRecentlyCreated) - 对值做标准化处理:字符串 trim、数字转 float/int、null 和空字符串是否等价需按业务定
- 示例片段:
$a = $recordA->getOriginal();<br>$b = $recordB->getOriginal();<br>$keys = array_intersect_key($a, $b);<br>$diff = [];<br>foreach ($keys as $k => $v) {<br> if ($a[$k] !== $b[$k]) {<br> $diff[$k] = ['old' => $a[$k], 'new' => $b[$k]];<br> }<br>}
用 array_diff_assoc() 快速初筛,但要注意键值类型陷阱
直接 array_diff_assoc($a, $b) 看起来快,但它对类型敏感:比如数据库里存的 0(int)和 PHP 读出来可能是 "0"(string),就会被当成不同。
使用场景:适合结构清晰、字段类型稳定的配置表或字典表;不适合用户输入频繁、类型混杂的内容表。
实操建议:
- 先统一类型:
array_map('strval', $arr)或按字段类型映射(如用intval()处理 ID 字段) - 忽略某些字段:用
unset()剔除update_time、id等非业务字段再比 - 注意浮点数精度,
float直接比容易失败,建议用abs($a - $b) 判断
TP6 中用 toJson() + JSON Patch 思路不现实
有人想用 json_encode() 后跑 json-diff 库,听起来高级,实际在 TP6 里问题一堆:模型里的闭包、资源句柄、循环引用会让 toJson() 报错;而且 JSON 不保留字段顺序,diff 结果难定位。
性能影响明显:一次 toJson() 可能触发全部关联加载,尤其带 with() 的模型,开销远超直接取 getOriginal()。
实操建议:
- 放弃 JSON 方案,除非你已把模型彻底扁平化为纯数组
- 如果真需要结构化差异输出(比如审计日志),用上面的手动遍历 + 字段白名单,可控且轻量
- 别在生产环境临时
dump()模型看结构——__isset()和魔术方法会让输出失真
事情说清了就结束。真正麻烦的从来不是“怎么比”,而是“哪些字段该比、哪些该忽略、null 和空字符串算不算变”。这些必须贴着你的表结构和业务规则来定,没法套模板。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










