删除具有外键的列时出现Laravel错误:常规错误:1025重命名错误

149

我使用迁移创建了一个表格,代码如下:

public function up()
{
    Schema::create('despatch_discrepancies',  function($table) {
        $table->increments('id')->unsigned();
        $table->integer('pick_id')->unsigned();
        $table->foreign('pick_id')->references('id')->on('picks');
        $table->integer('pick_detail_id')->unsigned();
        $table->foreign('pick_detail_id')->references('id')->on('pick_details');
        $table->integer('original_qty')->unsigned();
        $table->integer('shipped_qty')->unsigned();
    });
}

public function down()
{
    Schema::drop('despatch_discrepancies');
}

我需要修改这个表并删除外键引用和列pick_detail_id,然后在pick_id列后添加一个新的varchar列名为sku

所以,我已经创建了另一个迁移,看起来像这样:

public function up()
{
    Schema::table('despatch_discrepancies', function($table)
    {
        $table->dropForeign('pick_detail_id');
        $table->dropColumn('pick_detail_id');
        $table->string('sku', 20)->after('pick_id');
    });
}

public function down()
{
    Schema::table('despatch_discrepancies', function($table)
    {
        $table->integer('pick_detail_id')->unsigned();
        $table->foreign('pick_detail_id')->references('id')->on('pick_details');
        $table->dropColumn('sku');
    });
}

运行这个迁移时,我得到了以下错误:

[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1025 Error on rename of './dev_iwms_reboot/despatch_discrepancies' to './dev_iwms_reboot/#sql2-67c-17c464' (errno: 152) (SQL: alter table despatch_discrepancies drop foreign key pick_detail_id)

[PDOException]
SQLSTATE[HY000]: General error: 1025 Error on rename of './dev_iwms_reboot/despatch_discrepancies' to './dev_iwms_reboot/#sql2-67c-17c464' (errno: 152)

当我尝试通过运行php artisan migrate:rollback命令来撤销此迁移时,我会收到一个Rolled back的消息,但实际上它并没有在数据库中执行任何操作。

有任何想法是什么出了问题吗?如何删除具有外键引用的列?

8个回答

264

你可以使用这个:

Schema::table('despatch_discrepancies', function (Blueprint $table) {
    $table->dropForeign(['pick_detail_id']);
    $table->dropColumn('pick_detail_id');
});

如果你查看dropForeign源代码,当你将列名作为数组参数传递时,它会为你构建外键索引名称。


4
被接受的答案也是可行的:您必须使用正确的索引名称约定。但这也是该答案的问题:您必须记住索引的命名方案,而此解决方案会自动执行!我一直使用另一种方法,并且一直抱怨它有多么不实用。现在我立即切换到这个解决方案。非常感谢! Translated: 被接受的答案也是可以的:您必须使用正确的索引名称约定。但这也是那个答案的问题:您必须记住索引的命名方案,而这个解决方案会自动执行!我以前总是使用另一种方法,并且一直抱怨它有多么不方便。现在我立即转用这个解决方案。非常感谢! - Marco Pallante
8
太棒了!我一直像个傻瓜一样走弯路。Laravel 的文档确实需要一些帮助。也许我可以接受这个挑战… - simonhamp
1
在Laravel 5.0中对我很有用。非常感谢,Alex! - SilithCrowe
3
这是一个很巧妙的技巧,比记住外键命名约定(可能会在未来更改)要友好得多。就像@ronin1184所说,在Laravel 5.2中运行得非常完美。 - Robin van Baalen
1
适用于5.4!已检查文档,此功能自5.1版本以来就存在:“您可以传递一个数组值,当删除时将自动使用传统的约束名称”。https://laravel.com/docs/5.1/migrations#foreign-key-constraints - Arian Acosta
显示剩余8条评论

137

原来,当您像这样创建外键时:

$table->integer('pick_detail_id')->unsigned();
$table->foreign('pick_detail_id')->references('id')->on('pick_details');

Laravel 独特地按照以下方式命名外键引用:

<table_name>_<foreign_table_name>_<column_name>_foreign
despatch_discrepancies_pick_detail_id_foreign (in my case)

因此,当您想删除具有外键引用的列时,您必须这样做:

$table->dropForeign('despatch_discrepancies_pick_detail_id_foreign');
$table->dropColumn('pick_detail_id');

更新:

Laravel 4.2+ 引入了一种新的命名规范:

<table_name>_<column_name>_foreign

更新:

Laravel > 8.x 引入了一个新函数

dropConstrainedForeignId('pick_detail_id');

这将删除该列以及该列的外键。


4
不适用于Laravel 4.2版本。 <foreign_table_name>不是关键字之一。只需要使用<table_name>_<column_name>_foreign即可。 - rich remer
我在laravel 4.2中使用它,现在仍在使用,对我很有效。 - Latheesan
2
"<table_name>_<column_name>_foreign" 的命名规则在 5.1 版本中仍然有效。 - Yahya Uddin
显然,在删除关系约束之后,您还必须删除该列。我认为文档也应该包括这一点,因为人们很容易会认为dropForeign也会删除该列。感谢分享。 - fokosun
还有别忘了前缀。Laravel 也不会自动添加它们。 - Sumit Wadhwa
显示剩余2条评论

34

我的表中有多个外键约束,我必须一个一个地删除外键约束,通过将列名作为数组索引在down方法中传递:

public function up()
{
    Schema::table('offices', function (Blueprint $table) {
        $table->unsignedInteger('country_id')->nullable();
        $table->foreign('country_id')
            ->references('id')
            ->on('countries')
            ->onDelete('cascade');

        $table->unsignedInteger('stateprovince_id')->nullable();
        $table->foreign('stateprovince_id')
            ->references('id')
            ->on('stateprovince')
            ->onDelete('cascade');
        $table->unsignedInteger('city_id')->nullable();
        $table->foreign('city_id')
            ->references('id')
            ->on('cities')
            ->onDelete('cascade');
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    Schema::table('offices', function (Blueprint $table) {
        $table->dropForeign(['country_id']);
        $table->dropForeign(['stateprovince_id']);
        $table->dropForeign(['city_id']);
        $table->dropColumn(['country_id','stateprovince_id','city_id']);
    });
} 

使用下面的语句不起作用

$table->dropForeign(['country_id','stateprovince_id','city_id']); 

因为dropForeign并不认为它们是我们想要删除的单独列,所以我们必须逐个删除它们。


谢谢我的朋友,将列名添加到数组中对我很有帮助。 - Pierre
如果有人想知道,MySQL为外键自动创建的索引在列被删除时也会被删除。不需要使用 $table->dropIndex('column_name') 手动删除它们。 - Aleksandar
谢谢,我非常需要 Schema::table('offices', function (Blueprint $table) {} ... - Akshay K Nair
由于某些原因,Laravel在dropForeign中没有为表名添加前缀并添加“foreign”后缀。因此,我认为应该这样写:$table->dropForeign(['offices_country_id_foreign']); - Sumit Wadhwa

10

对我来说解决这个问题的关键是确保 $table->dropForeign() 命令传递的是正确的关系名称,而不是列名称。我认为你不应该传递列名称,因为这会更加直观。

对我有效的方法是:

$table->dropForeign('local_table_foreign_id_foreign');
$table->column('foreign_id');

因此,我传递给dropForeign()的字符串格式如下:

[本地表]_[外键字段]_foreign

如果您可以使用Sequel Pro或Navicat等工具进行可视化,则能够更好地理解上述内容。


这个可以正常工作,但我发现与@Alex建议的在括号中包围表格相比,它不太直观。 - Mark Karavan

9

我想到的是,我不知道在哪里放置Schema::table块。

后来我发现关键在于SQL错误:

[Illuminate\Database\QueryException]
SQLSTATE[23000]: Integrity constraint violation: 1217 Cannot delete or update a parent row: a foreign key constraint fails (SQL: drop table if exists `lu_benefits_categories`)

因此,在`lu_benefits_categories`迁移的`down()`函数中,并在`Schema::dropIfExists`行之前添加`Schema::table`块:
public function down()
{
    Schema::table('table', function (Blueprint $table) {
        $table->dropForeign('table_category_id_foreign');
        $table->dropColumn('category_id');
    });
    Schema::dropIfExists('lu_benefits_categories');
}

接下来,php artisan migrate:refresh 或者 php artisan migrate:reset可以解决这个问题。


7
在 Laravel 8 中使用 dropConstrainedForeignId (https://github.com/laravel/framework/pull/34806) 来删除约束外键。
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddAddressFieldsInEventTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        
        Schema::table('events', function (Blueprint $table) {
            $table->bigInteger('address_id')->nullable();

            $table->foreign('address_id')
                ->references('id')
                ->on('addresses')
                ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('events', function (Blueprint $table) {
            $table->dropConstrainedForeignId('address_id');
            $table->dropColumn('address_id');
        });
    }
}


2
在 Laravel > 8.x 中,您可以使用以下代码来添加/删除外键。在删除列之前,您需要删除外键,否则外键可能会导致错误。

添加外键


请按照以下格式进行操作:
 $table->foreignId('pick_id')->constrained('picks')->cascadeOnUpdate()
->cascadeOnDelete();

删除外键

  $table->dropConstrainedForeignId('pick_id');

2

你可以先禁用关系 ID

Schema::disableForeignKeyConstraints();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接