You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 8:使用Factory播种时查询表失败引发唯一键冲突

解决方案

1. 禁用播种器事务

Laravel 播种器默认会将整个播种过程包裹在事务中,所有记录要等到全部生成完成后才会批量写入数据库,这就导致你的 slug 生成逻辑无法查询到已生成的重复值。可以在播种器中显式禁用事务:

修改 ArticlesTableSeeder 的 run 方法:

class ArticlesTableSeeder extends Seeder
{
    public function run(): void
    {
        // 禁用事务,每条记录创建后立即写入数据库
        $this->withoutTransaction();
        
        Article::factory()->count(10000)->create();
    }
}

这样每次创建 Article 实例时,记录会立刻提交到数据库,你的 setSlugAttribute 修改器就能查询到之前生成的 slug,自动追加序号避免重复。

2. 分批创建记录

如果不想完全禁用事务,可以将大数量的创建任务拆分成多个小批次,每批次创建完成后提交事务,确保下一批次能查询到之前的 slug:

class ArticlesTableSeeder extends Seeder
{
    public function run(): void
    {
        // 分100批,每批创建100条
        for ($batch = 0; $batch < 100; $batch++) {
            Article::factory()->count(100)->create();
        }
    }
}

每一批次的创建都会独立提交,下一批次生成 slug 时就能查询到之前所有批次的记录,避免唯一键冲突。

3. 内存中跟踪已生成的slug(进阶方案)

如果追求更高的性能(减少数据库查询次数),可以在播种器中维护一个内存数组,记录已经生成的 slug,生成时先检查内存中的值,再回查数据库:

class ArticlesTableSeeder extends Seeder
{
    public function run(): void
    {
        $usedSlugs = [];

        Article::factory()->count(10000)->make()->each(function ($article) use (&$usedSlugs) {
            $baseSlug = $article->slug;
            $currentSlug = $baseSlug;
            $counter = 1;

            // 先检查内存中的slug,再检查数据库
            while (in_array($currentSlug, $usedSlugs) || Article::where('slug', $currentSlug)->exists()) {
                $currentSlug = "{$baseSlug}-{$counter}";
                $counter++;
            }

            $article->slug = $currentSlug;
            $usedSlugs[] = $currentSlug;
            $article->save();
        });
    }
}

这种方式先用 make() 创建模型实例不写入数据库,在内存中处理 slug 去重后再调用 save() 写入,既避免了事务问题,又减少了重复查询数据库的次数。


内容的提问来源于stack exchange,提问作者Matt Komarnicki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 15:31:41