updateorcreate()需用唯一稳定字段作查找条件,否则易重复创建;第二个参数会无条件写入,未传字段可能被置空;批量操作应改用upsert();外部输入须预处理空格和大小写。

直接用 updateOrCreate() 就能解决“有就更新、没有就创建”的需求,但必须理解它底层是先 where 查、再决定 insert 还是 update;误用会导致重复插入、条件漏判或字段被清空。
updateOrCreate() 的 where 条件必须唯一且稳定
它靠传入的第一个参数(数组)做查找依据,比如 ['email' => $request->email]。如果这个字段没加数据库唯一索引,或者业务上允许重复(如多个用户共用一个邮箱),那就会反复创建新记录——不是方法错了,是条件本身不可靠。
- 推荐用主键或带
UNIQUE约束的字段,例如['uuid' => $data['uuid']]或['external_id' => $data['id']] - 避免用可能变更的字段,比如
['name' => $input]—— 名字改了就变成新记录 - 不要混用动态值和固定值,
['status' => 'pending', 'email' => $e]会让逻辑变难懂,也容易漏掉索引覆盖
第二个参数是“要写入的字段”,不参与查找
你传进来的更新/创建字段(第二个数组)会无条件写入,哪怕该字段在数据库里是 NOT NULL 但没传值,也会被设为 NULL(除非模型有 $casts 或访问器干预)。
- 常见坑:
updateOrCreate(['email' => $e], ['name' => $n])会把其他字段(如status、avatar)全置空,因为没显式传进去 - 安全做法:只传真正需要更新的字段;若要保留旧值,得先
find()再fill()->save() - 软删除模型要注意:默认查不到已删除记录,得加
withTrashed()才能命中
和 firstOrCreate()、updateOrInsert() 的关键区别
三者看起来像,但行为边界很清晰:
-
firstOrCreate():只创建,不更新;找到就返回模型实例,没找到才新建并保存 -
updateOrCreate():找到就update(触发模型事件),没找到才create(也触发事件) -
updateOrInsert():原生查询构建器方法,不走模型层,不触发任何事件,也不校验$fillable;适合高频写入日志、统计类数据
比如埋点数据入库,用 updateOrInsert() 更快;但用户资料同步,必须用 updateOrCreate() 保证事件和验证生效。
批量处理时别硬套 updateOrCreate()
循环调用 updateOrCreate() 处理上百条数据,会触发同等次数的 SQL 查询(先 SELECT 再 INSERT/UPDATE),性能断崖式下跌。
- 替代方案:用
upsert()(Laravel 9.2+),一条语句完成批量“存在则更新、不存在则插入” - 注意
upsert()的$uniqueBy参数必须对应数据库的唯一索引列,否则报错或行为异常 - 示例:
User::upsert($data, ['email'], ['name', 'updated_at']),其中email是唯一索引字段,name和updated_at是冲突时要更新的字段
真正容易被忽略的是:无论用哪个方法,只要涉及外部输入(尤其是用户提交的 email、phone、code),都得提前过滤空格、大小写、特殊字符——数据库索引对空白和大小写敏感,'abc@example.com ' 和 'abc@example.com' 在唯一约束下就是两条不同记录。











