我正在我的 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]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "communications_categories_pkey"
DETAIL: Key (id)=(2) already exists. (SQL: insert into "communications_categories" ("name", "region_id", "updated_at", "created_at") values (Alerts, 1, 2018-06-19 09:38:20, 2018-06-19 09:38:20) returning "id")
它似乎在尝试分配主键 ID 2(当它已经存在时)——但表结构在主键上有一个 nextval,我认为它会采用最后添加的 ID 并在之后创建一个新 ID。根据关于 Eloquent 插入的 Laravel 文档 here没有提到必须自己规定实际的主键id,可填充的元素只有name
和region_id
。
在此方面的任何帮助表示赞赏,因为我对 Laravel 和 Eloquent 数据库方法相当陌生。如果有帮助,我正在使用 PostgreSQL 数据库。
最佳答案
另一个答案似乎假定您需要向您解释 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
值(value)观。
DB::insert($sql); // will return true
如果您需要返回 Eloquent 对象,我建议包括 returning *
或 returning id
并将其传递给 CommunicationsCategories::findOrFail($values[0]['id'])
.将复杂的插入语句放入 Eloquent select 中可能需要大量工作。 Eloquent 还有一个名为 hydrate 的函数,无需额外的 SQL 调用即可工作。
SQL 语句将插入所需的值,除非在唯一约束上存在冲突,然后它将通过更新将被冲突排除的值从插入应用到预先存在的行。
关于Laravel firstOrCreate 方法抛出重复 ID 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50924236/