Laravel 8中生成含两个外键的假数据时主键重复报错如何解决
问题原因
你的users_booking表使用user_id和user_id_booking作为联合主键,要求二者的组合值全局唯一。你当前的工厂逻辑仅保证了两个ID不会相等,但没有校验生成的ID组合是否已经被插入到表中,多次随机抽取命中相同组合时就会抛出主键重复错误。
另外你每次循环都调用User::all()查询全表用户,数据量大时会有严重性能问题,也可以同步优化。
解决方案
下面提供3种常用的解决方式,按需选择即可:
方案1:工厂内增加组合唯一性校验
修改工厂代码,每次生成ID组合后判断是否已存在,不存在才返回,改动最小:
public function definition() { // 提前取出所有用户ID,避免重复查询数据库 $userIds = User::pluck('id')->toArray(); do { $userId = $userIds[array_rand($userIds)]; $userIdBooking = $userIds[array_rand($userIds)]; // 校验两个ID不相等,且组合不存在 $exists = UserBooking::where('user_id', $userId) ->where('user_id_booking', $userIdBooking) ->exists(); } while ($userId === $userIdBooking || $exists); return [ 'user_id' => $userId, 'user_id_booking' => $userIdBooking, 'created_at' => $this->faker->dateTimeInInterval('-90 days', '+ 90 days') ]; }
方案2:预生成不重复组合批量插入
这种方式性能最高,完全避免重复校验的查询开销,适合生成大量测试数据的场景,仅需要修改填充逻辑,不需要改动工厂代码:
// 生成500个用户 $users = User::factory(500)->create(); $userIds = $users->pluck('id')->toArray(); $bookingPairs = []; // 生成100个不重复的组合 while (count($bookingPairs) < 100) { $a = $userIds[array_rand($userIds)]; $b = $userIds[array_rand($userIds)]; if ($a === $b) continue; // 如果user_id=1、user_id_booking=2 和反过来算两条不同记录,就把下一行换成 $key = $a . '-' . $b $key = min($a,$b) . '-' . max($a,$b); if (!isset($bookingPairs[$key])) { $bookingPairs[$key] = [ 'user_id' => $a, 'user_id_booking' => $b, 'created_at' => fake()->dateTimeInInterval('-90 days', '+ 90 days') ]; } } // 批量插入 UserBooking::insert(array_values($bookingPairs));
方案3:用firstOrCreate避免报错
如果不需要严格生成100条数据,允许重复时自动跳过,可以在工厂调用时用createOrFirst(Laravel 9及以上版本支持),不会抛出中断错误:
User::factory(500)->create(); for ($i=0; $i<100; $i++) { UserBooking::factory()->createOrFirst(); }
内容的提问来源于stack exchange,提问作者pwnz
相关产品推荐
相关产品推荐

