Laravel 8执行数据库迁移报errno 150外键约束错误的问题
你遇到的SQLSTATE[HY000]: General error: 1005 Can't create table... Foreign key constraint is incorrectly formed是MySQL创建外键失败的通用报错,结合你给出的代码,按优先级排查修复即可,不需要复杂操作:
核心触发原因
这也是Laravel开发中外键报错最高发的两类问题:
- 关联字段类型不匹配:你给
users表设置的主键是$table->bigIncrements('id'),该字段在MySQL中默认生成无符号大整数(BIGINT UNSIGNED);但你在profiles表里给user_id的定义是$table->bigInteger('user_id'),Laravel默认的bigInteger是有符号大整数,两个字段的符号属性、数据类型对不上,MySQL会直接拒绝创建这个外键,和报错信息里卡住的第一个外键完全对应。 - 同类问题还藏在其他关联字段里:
nationality_id、country_id、state_id、city_id都用了普通integer类型,默认是有符号整数,和对应父表的无符号主键类型不匹配,就算你修好user_id的问题,后面创建这几个外键的时候还是会陆续报同样的错。 - 迁移执行顺序不满足MySQL外键要求:MySQL创建外键时要求被关联的父表必须已经存在,才能创建子表的外键。如果
profiles表的迁移文件时间戳早于users、countries、state_origins、cities这些关联父表的迁移时间,跑迁移时会先创建profiles表,找不到对应的父表自然会报错。
对应修复操作
1. 修正外键字段的定义
最稳妥的写法是用Laravel内置的foreignId方法定义关联字段,这个方法生成的unsignedBigInteger和bigIncrements主键是完全匹配的,还能链式调用constrained自动绑定父表关联,省得手动写外键时搞错类型:
public function up() { Schema::create('profiles', function (Blueprint $table) { $table->bigIncrements('id'); // 替换原来的bigInteger('user_id'),类型完全匹配users表主键 $table->foreignId('user_id'); $table->string('first_name',50); $table->string('last_name',50); $table->string('other_name',50)->nullable(); $table->string('gender',20); $table->string('user_photo',350)->nullable(); $table->foreignId('nationality_id'); $table->string('marital_status',50); $table->date('dob')->nullable(); $table->string('address', 300)->nullable(); // 可空的关联字段记得加nullable属性 $table->foreignId('country_id')->nullable(); $table->foreignId('state_id')->nullable(); $table->foreignId('city_id')->nullable(); $table->string('cv_file',350)->nullable(); $table->text('summary')->nullable(); $table->timestamps(); // 手动定义外键的写法和上面的自动关联效果一致,按需保留即可 $table->foreign('user_id')->references('id')->on('users'); $table->foreign('nationality_id')->references('id')->on('countries'); $table->foreign('country_id')->references('id')->on('countries'); $table->foreign('state_id')->references('id')->on('state_origins'); $table->foreign('city_id')->references('id')->on('cities'); }); }
注意:如果你关联的
countries、state_origins、cities表主键不是bigIncrements,而是普通的increments(对应MySQL的INT UNSIGNED),要把foreignId替换成unsignedInteger,确保字段类型和父表完全一致。
2. 核对迁移文件的执行顺序
检查所有关联父表的迁移文件名时间戳,确保users、countries、state_origins、cities这几个表的迁移文件时间,早于profiles表迁移文件的时间,避免跑迁移时先创建profiles、再创建关联父表。
3. 清理数据库里的残留表结构
之前执行失败的迁移会在数据库里留下空的profiles表(虽然外键没创建成功,但表结构已经存在),这时候直接改完代码重跑迁移还是会报错:
- 可以直接进数据库删掉残留的空
profiles表 - 或者执行
php artisan migrate:rollback回滚之前未成功的迁移操作,清空残留结构后再跑php artisan migrate。
快速排错技巧
如果改完上述内容依然报错,可以逐行注释外键定义,从第一个外键开始放开验证,定位到具体是哪个外键关联出问题,针对性检查对应父表是否存在、主键类型是否匹配即可,不需要全部重写。
内容的提问来源于stack exchange,提问作者user11352561

