Php Laravel在事务内插入父级子级失败

Php Laravel在事务内插入父级子级失败,php,laravel,postgresql,transactions,foreign-keys,Php,Laravel,Postgresql,Transactions,Foreign Keys,我正在尝试执行2个插入,在2个表中,表通过外键进行约束。这两个操作必须在事务内部执行,以防止最终失败。(实际上,我需要在更多的表上执行更多的插入操作,因此事务非常重要;但本例中的2个表足以复制问题) 数据库驱动程序是pgsql SomeRepo.php(也尝试使用事务关闭变量) Parent.php protected $fillable = [ 'name' ]; public function children() { ret

我正在尝试执行2个插入,在2个表中,表通过外键进行约束。这两个操作必须在事务内部执行,以防止最终失败。(实际上,我需要在更多的表上执行更多的插入操作,因此事务非常重要;但本例中的2个表足以复制问题)

数据库驱动程序是pgsql

SomeRepo.php(也尝试使用事务关闭变量)

Parent.php

    protected $fillable = [
        'name'
    ];

    public function children()
    {
        return $this->hasMany(Child::class);
    }
    protected $fillable = [
        'name', 'parent_id'
    ];

Child.php

    protected $fillable = [
        'name'
    ];

    public function children()
    {
        return $this->hasMany(Child::class);
    }
    protected $fillable = [
        'name', 'parent_id'
    ];

尝试插入返回父id的子行时执行失败

insert or update on table "child" violates foreign key constraint "child_parent_id_foreign"
编辑 子表SQL:

DROP TABLE IF EXISTS "public"."child";
CREATE TABLE "public"."child" (
  "id" int4 NOT NULL DEFAULT nextval('child_id_seq'::regclass),
  "parent_id" int4 NOT NULL,
  "is_read" bool NOT NULL DEFAULT false,
  "created_at" timestamp(0) DEFAULT now(),
  "updated_at" timestamp(0),
  "deleted_at" timestamp(0)
)
;
ALTER TABLE "public"."child" OWNER TO "my_user";

-- ----------------------------
-- Primary Key structure for table child
-- ----------------------------
ALTER TABLE "public"."child" ADD CONSTRAINT "child_pkey" PRIMARY KEY ("id");

-- ----------------------------
-- Foreign Keys structure for table child
-- ----------------------------
ALTER TABLE "public"."child" ADD CONSTRAINT "child_parent_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "public"."parent" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED;

子函数需要一个中间表将其更改为:

 public function children()
{
    return $this->belongsToMany(Child::class);
}
不要这样做
$parent->children()->save($child)


或者,如果您想这样做,请使用带有两个字段的
child\u id
parent\u id
将数据保存在相关表中

Parent::create(['name'=>'parent name']); //save in parent table
$lastId = Parent::query()->max('id'); //get last inserted row id

$parent = App\Parent::find($lastId);

$child = $parent->children()->create([
    'message' => 'A new comment.',
]);
您还可以使用
createMany
方法

$parent = App\Parent::find($lastId);

$parent->children()->createMany([
    [
        'message' => 'A new comment.',
    ],
    [
        'message' => 'Another new comment.',
    ],
]);

在父模型的关系上使用
create
createMany
使用此代码进行尝试:

// Create the parent object.
$parent = Parent::create([
  'name' => 'Parent 1'
]);

// Insert one.
$child = $parent->children()->create([
    'name' => 'Child 1',
]);

// Insert many.
$parent->children()->createMany([
    [
        'name' => 'Child 2',
    ],
    [
        'name' => 'Child 3',
    ],
]);
你可以试试这个

try {
     DB::beginTransaction();
       $parent = Parent::create([
     'name' => 'Parent name'
     ]);
     $parent->children()->create([
       'parent_id' => $parent->id,
       'name' => 'Child name'
     ]);
     DB::commit();
 } catch (Exception $e) {
     DB::rollback();
 }
更改外键--在数据库上运行此sql

alter table child drop constraint child_parent_id;

alter table child add foreign key (parent_id) references parent(id) deferrable initially deferred;

这将允许您按任意顺序创建子项或父项,并且在提交之前不会验证约束。在这种情况下不需要这样做,但这取决于id的生成方式。

我的场景使用一对多。如果多对多关系没有用处,我不想实现它。为什么要使用最后一个id来获取父模型?根据雄辩的文档()
Parent::create()
返回创建的模型,因此不需要这两行额外的代码(因为使用的最后一个id不是100%安全的,所以很危险)。不需要查询最大id。尝试了你的代码,它给出了相同的外键错误。您是否尝试在PostgreSQL数据库上运行它?如果没有事务,这肯定会起作用。但正如您在原始问题中所看到的,我需要使用PostgreSQL数据库在事务中完成所有操作。您能告诉我们数据库的结构吗?我认为这是一个鸡和蛋的问题,即在PostgreSQL中插入带有循环引用的表时,请尝试在->save()之后转储$parent->id。。。确保数据确实存在。请阅读此问题的注释,然后您也可以检查此代码与原始问题中的代码有何不同?我已经尝试过了,但仍然出现相同的错误:
insert或update on table“child”违反外键约束“child\u parent\u id\u fkey”↵详细信息:表“parent”中不存在键(parent_id)=(254)
我的id由以下序列生成:
默认值:nextval('parent_id_seq'::regclass)
检查子表上的外键。也许这是错误的。您可以在DDL(用于创建外键的SQL)中粘贴外键约束吗?不是这样,就是插入的数据不正确。。也就是说,子表插入不是插入您刚才创建的父键。这实际上更有意义,因为如果您以正确的顺序创建此数据,通常不必执行延迟约束,而且根据您的来源,您是。我使用用于创建子表的SQL更新了此问题。我还记录了创建的父表的id,它与在返回FK错误的子表上执行的插入中存在的父表id字段相匹配。