Laravel/Lumen外键约束冲突:如何定义避免插入报错?
先给你拆解下这个错误的核心:你要插入的profiles记录里,category字段填的2,在关联的categories表中找不到对应的id记录。外键约束的本质就是管这个——子表(profiles)的关联字段必须在父表(categories)里有对应值,所以咱们先从数据层面解决当前问题,再聊聊怎么优化外键定义避免后续踩坑。
一、先搞定当前插入失败的问题
第一步:检查categories表有没有id=2的记录
你可以跑个SQL查一下:SELECT * FROM categories WHERE id = 2;如果结果是空的,那就是问题根源了——得先往
categories表里插一条id=2的分类数据,比如:INSERT INTO categories (id, name, abbrv, created_at, updated_at) VALUES (2, '测试分类', 'TEST', NOW(), NOW());插完这条再去插profiles的数据,就能成功了。
第二步:后续插入要保证category值合法
以后往profiles插数据时,一定要确认category填的是categories表中已经存在的id,不能随便写个不存在的数。
二、优化外键约束定义(减少未来的坑)
从你给的迁移文件来看,外键定义本身是没问题的,但可以做几个小优化让它更健壮:
统一主键类型(可选但推荐)
你的categories表用的是integer主键,而profiles表主键是bigIncrements(对应bigint类型)。虽然当前外键类型是匹配的(都是unsigned integer),但长远来看统一类型更稳妥,比如把categories的主键改成bigIncrements:// categories迁移文件的up方法 public function up() { Schema::create('categories', function (Blueprint $table) { $table->bigIncrements('id'); // 和profiles主键类型统一 $table->string('name'); $table->string('abbrv'); $table->timestamps(); }); }同时把profiles表的
category字段改成unsignedBigInteger:// profiles迁移文件的up方法 $table->unsignedBigInteger('category'); // 对应bigint unsigned类型允许category为空(如果业务需要)
要是你的业务允许用户不选分类,可以给category字段加nullable(),同时调整外键的删除行为:// profiles迁移文件中 $table->integer('category')->unsigned()->nullable(); $table->foreign('category') ->references('id') ->on('categories') ->onDelete('set null'); // 当分类被删除时,把profile的category设为null这样插入时category填null也不会触发约束错误。
确认迁移文件的执行顺序
外键依赖父表存在,所以categories的迁移文件必须比profiles的先执行。Laravel是按迁移文件名的时间戳前缀排序的,所以要保证categories的迁移文件时间戳更早,比如2020_04_01_000000_create_categories_table.php要早于2020_04_02_000000_create_profiles_table.php,不然创建profiles表时categories表还没建,外键约束会创建失败。
总结
当前的错误就是数据不匹配,先把categories表的对应记录补上就行;之后通过优化外键定义和迁移顺序,能让整个约束体系更稳,减少后续的约束报错。
内容的提问来源于stack exchange,提问作者salimsaid

