ThinkPHP集合操作需用Collection专用方法:判空必须用isEmpty(),diff/intersect要求同模型同字段,批量更新应避免each而用update或saveAll。

ThinkPHP 的集合操作不是 PHP 原生数组函数,而是模型 select() 返回的 think\model\Collection 对象提供的专用方法,用错类型或误判空值会直接导致逻辑异常。
判断数据集是否为空必须用 isEmpty()
很多人写 if (empty($users)) 或 if (!$users),结果永远走不通——因为 Collection 对象本身不为空,只是内部没数据。它实现了 Countable 和 IteratorAggregate,但不响应 empty() 的底层判断逻辑。
$users = User::where('status', 0)->select();- ✅ 正确判断:
if ($users->isEmpty()) { ... } - ❌ 错误写法:
if (empty($users))、if (!$users)、if (count($users) === 0)(虽然 count 可用,但语义不清且多一次调用) - 注意:
Collection的isEmpty()是 O(1),不遍历,放心用
diff() 和 intersect() 只支持同结构数据集
这两个方法要求参与运算的两个 Collection 来自相同模型、字段一致(至少主键和用于比对的字段要存在),否则会静默失败或返回意外结果。
- ✅ 安全用法:
$list1 = User::where('status', 1)->field('id,name')->select(); $list2 = User::where('name', 'like', '%admin%')->field('id,name')->select(); $diff = $list1->diff($list2); - ❌ 危险操作:
$list1->diff(Db::name('order')->select())—— 混用Db和模型返回值,类型不同,diff()会抛BadMethodCallException - 字段缺失时(比如
$list2没有id字段),diff()默认按数组索引对比,结果不可信 - 如需跨表比对,先统一转成二维数组:
$arr1 = $list1->toArray(); $arr2 = $list2->toArray(); array_diff_assoc($arr1, $arr2);
批量更新/删除前别忘了用 each() 或 map() 预处理
Collection 提供了链式遍历能力,但直接在循环里调用 save() 或 delete() 效率极低,且无法利用模型的批量接口。
立即学习“PHP免费学习笔记(深入)”;
- ❌ 低效写法:
$users->each(function ($user) { $user->status = 2; $user->save(); });—— N 次 SQL - ✅ 推荐组合:
$ids = $users->column('id'); User::update(['status' => 2], ['id' => ['in', $ids]]); - 如果需要逐条计算逻辑(比如 status 依赖 created_time),可用
map()构造新数据:$data = $users->map(function ($u) { return ['id' => $u->id, 'status' => $u->created_time > time() - 86400 ? 1 : 0]; })->toArray(); User::saveAll($data, false, true); -
saveAll()第三个参数设为true表示使用replace,注意主键冲突行为
集合方法看着像数组操作,但背后绑着模型生命周期、事件钩子和字段映射规则;绕过模型直接用 toArray() 再处理,就等于放弃自动时间戳、验证、软删除这些关键保障。



















