Laravel迁移禁用外键检查的好方法

64

在运行Laravel迁移时,我遇到了一些小问题。我使用的是Laravel 5.1版本。

由于有很多具有许多关系的表格,因此可能无法重命名迁移文件以便以正确的顺序运行它们,以避免违反外键约束。这是我过去曾经做过的事情,非常不方便。

现在我正在将每个迁移定义为:

class CreateSomeTable extends Migration
{
    public function up()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
        // my table definitions go here
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
    }

    public function down()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
        // drop table
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
    }
}

问题是编写起来很繁琐,而且会使代码变得混乱。

我也考虑过创建两个虚拟的迁移文件,它们唯一的目的就是启用和禁用外键检查,并且我会以这样的方式命名它们,使它们在每次迁移的开始和结束时运行。

如果有一个优雅的解决方案,那么是否可能将其应用于种子数据的处理过程中,因为在那里也经常遇到这个问题。

这显然是一个非常临时的解决方案,我想知道是否有更好的方法。是否有一些beforeMigrateafterMigrate方法可以覆盖,或者类似这样的方法?

如果没有,你是如何解决这个问题的?

任何见解都将不胜感激,我不喜欢我所提出的所有选项。


3
如果您使用artisan创建迁移并且它们具有正确的时间戳,则它们应始终按正确的顺序运行。它们按时间戳的顺序运行。 - Karl
1
是的,当我提到文件顺序时就是这个意思。问题在于有时您无法按正确顺序生成它们,或者有时可能存在循环依赖,即两个表都具有相互引用的外键。无论以何种顺序运行该示例,除非禁用检查,否则都会出现外键约束违规。 - Pavlin
4
避免这些问题的最佳方法是先创建一个带有表格创建的迁移,然后再创建/删除键的另一个迁移。 - Maxim Lanin
虽然不是特别相关,但你也可以通过以下方式禁用外键检查:\DB::getSchemaBuilder()->disableForeignKeyConstraints()。不确定是否有更好的调用方法。 - x-yuri
我又遇到了完全相同的问题,这次是在另一个项目中。作为一个临时的hack,我正在按照OP建议的做法进行操作; 创建2个虚拟迁移文件,分别在所有其他文件之前和之后运行,并在开头和结尾关闭和打开外键检查。我认为Laravel的迁移系统基本上存在缺陷。在所有这些文件中分散的表创建代码很丑陋且不方便。从现在开始,我将把所有内容都放在一个单独的迁移文件中,在开头和结尾禁用和启用外键检查,并清晰地列出所有模式逻辑。 - Inigo
3个回答

100

当Lumen/Laravel开始使用Passport时,我面临着类似的任务,我必须放弃之前从lucadegasperi/oauth2-server-laravel获取的OAuth服务器实现。

最终,我通过创建两个迁移来解决问题,第一个迁移清除了外键,第二个迁移实际上删除了表格。

在执行这些迁移之前,我必须使用Laravel's Passport(2016-06-01)之前的日期。

2016_05_31_000000_clear_old_oauth_relations.php

//...
class ClearOldOauthRelations extends Migration
{
    public function up()
    {
        Schema::disableForeignKeyConstraints();
        // drop foreign keys
        Schema::table('oauth_access_tokens', function (BluePrint $table) {
            $table->dropForeign('oauth_access_tokens_session_id_foreign');
        });
        //...
        Schema::enableForeignKeyConstraints();
    }
    //...
}

在第二个文件中 2016_05_31_000001_clear_old_oauth.php

//...
public function up()
{
    Schema::disableForeignKeyConstraints();
    Schema::drop('oauth_access_tokens');
    //...
    Schema::enableForeignKeyConstraints();
}
//...

这真的很好!这是最近才添加的功能吗?还是在laravel中已经有一段时间了,只是当我遇到这个问题时没有注意到它? - Pavlin
嗨@Pavlin,我不知道这是什么时候添加到Laravel / Lumen中的。请注意,您需要在例如PhpMyAdmin(结构->关系)中查找外键约束的名称。 - 4levels
自Laravel 5.2版本以来,这个功能被添加进去了。 - arielcr

7
我将把外键关系的逻辑提取到一个单独的迁移文件中,这有助于我做到以下几点:
  • 禁用外键约束。
  • 安全地删除数据库,如果它存在的话。
代码如下:
//file: 2017_06_19_230601_fk_postuser_table.php

public function down()
{
        Schema::disableForeignKeyConstraints();
        Schema::dropIfExists('post_user');
}

3

还有一个重要的方面需要记住,就是先删除外键,然后再删除列。如果先删除列,则会出现错误:

无法删除索引“tableName_columnName_foreign”:外键约束所需

正确的顺序很重要:

    public function down()
    {
        Schema::table('tableName', function (Blueprint $table) {
            $table->dropForeign(['columnName']); // fk first

            $table->dropColumn('columnName'); // then column
        });
    }

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