首页 > 解决方案 > Laravel firstOrCreate 方法抛出重复 ID 错误

问题描述

我正在我的 Laravel 项目中编写一个命令,该命令将类别插入数据库表,但取决于它们是否已经存在。

我调查了执行此操作的方法并遇到了firstOrCreate方法,因此我在命令中编写了以下内容:

$comCats = new CommunicationsCategories();

$comCats->firstOrCreate(
    ['name' => 'Job Updates'], ['region_id' => 1]);
$comCats->firstOrCreate(
    ['name' => 'Alerts'], ['region_id' => 1]);

基本上,我需要在区域 ID 为 1 的communications_categories 表中创建这两个类别。 Job Updates 类别已经存在,因此它按预期跳过了它,但是当它尝试创建不存在的 Alerts 类别时,我得到了我的控制台中出现以下错误:

SQLSTATE [23505]:唯一违反:7 错误:重复键值违反唯一约束“communications_categories_pkey”

详细信息:键 (id)=(2) 已存在。(SQL: insert into "communications_categories" ("name", "region_id", "updated_at", "created_at") 值 (Alerts, 1, 2018-06-19 09:38:20, 2018-06-19 09:38 :20) 返回“id”)

似乎它正在尝试分配一个已经存在的主键 ID 2 - 但是表结构在主键上有一个 nextval,我认为它会采用最后添加的 ID 并在此之后创建一个新 ID。根据关于 Eloquent Inserts 的 Laravel 文档,这里没有提到必须规定实际的主键 id 本身,并且可填充的元素只有nameand region_id

对此的任何帮助表示赞赏,因为我对 Laravel 和 eloqent 数据库方法相当陌生。如果有帮助,我正在使用 PostgreSQL 数据库。

标签: laravelpostgresqleloquent

解决方案


另一个答案似乎假设您需要向您解释 firstOrCreate ,而实际上您实际上在 Laravel 框架中遇到了一个错误,并且还假设Key (id)=(2)指的是region_id而不是您的自动递增主键id

您很可能希望在communications_categories.name.

您可以改为在 postgres 中使用此 SQL(假设您在名称上有唯一索引),这应该比 firstOrCreate 更可靠,但如果您计划将其他 laravel 功能与 firstOrCreate 一起使用(如观察者或更多查询构建器),它们的功能可能会丢失。

    INSERT INTO communications_categories (name, region_id, created_at, updated_at)
    VALUES ('Job Updates', 1, NOW(), NOW())
    ON CONFLICT (name) DO
    UPDATE SET region_id = excluded.region_id,
        updated_at = NOW()
    RETURNING *;

可以像这样使用:

    $values = DB::select($sql); // will return Array of StdClass

returning *如果您不需要这些值id,也可以不使用。

    DB::insert($sql); // will return true

如果您需要返回 Eloquent 对象,我建议您包含returning *orreturning id并将其传递给CommunicationsCategories::findOrFail($values[0]['id']). 将那个复杂的插入语句放入 Eloquent select 中可能需要做很多工作。Eloquent 还有一个叫做 hydrate 的函数,它可以在没有额外的 SQL 调用的情况下工作。

SQL 语句将插入所需的值,除非唯一约束存在冲突,否则它将通过更新将因冲突排除的值从插入应用到预先存在的行。


推荐阅读