导致错误的 Laravel 迁移不会回滚

问题描述 投票:0回答:3

我发现,在 Laravel 中构建数据库模式时,失败的迁移不会回滚,这使得迁移毫无意义。

例如,我有这样的迁移:

Schema::create('accounts', function(Blueprint $table)
{
    $table->increments('act_id');

    $table->string('act_name', 50)->unique();
    $table->boolean('act_active')->default(1);
    $table->unsignedInteger('act_type');
    $table->unsignedInteger('act_businesstype')->default(1);

    $table->timestamps();
});

Schema::table('accounts', function($table)
{
    $table->foreign('act_businesstype')->references('bst_id')->on('businesstypes');
});

无论如何,如果我运行该迁移,表创建得很好,但外键失败并且出现错误。没关系。我应该得到一个错误。但是,常识让我做出以下假设:

  1. 由于迁移失败,更改应自动回滚。嗯,他们没有。

好吧,所以

  1. 我应该调用 migrate:rollback 来撤消这些更改。好吧,没有发生该迁移的记录,所以我最终回滚了之前发生的迁移。

我在这里做错了什么吗?我想出如何“撤消”失败的迁移的唯一方法是实际进入数据库并删除表。当我处理复杂的模式时,我要来回修复错误,这是非常令人沮丧的。

所以,我想现在我已经有了我的小咆哮,我的问题是:

如何回滚引发错误的迁移?

database laravel-4
3个回答
4
投票

多年后回到这个问题,现在对数据库的工作原理有了更多了解:

迁移无法“自动回滚”。无法在事务中执行架构更改。仅 INSERTS、UPDATES 和 DELETES 可以回滚。

为了使其正常运行,需要有一个等效的“drop”脚本,在发生错误时运行以恢复架构更改。

编辑:这个问题似乎仍然受到一些关注,所以我想链接 Larvel 关于如何构建迁移文件的文档:https://laravel.com/docs/10.x/migrations#migration-struct,我将在这里发布代码供后代使用,以及如何处理错误的调整:

<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        try {
            Schema::create('flights', function (Blueprint $table) {
                $table->id();
                $table->string('name');
                $table->string('airline');
                $table->timestamps();
            });
        } catch (Exception $e) {
            $this->down();
            throw $e;
        }
    }
 
    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('flights');
    }
};

因此,如果您选择手动回滚迁移或发生错误,

down
函数将执行。

最后要注意的是

Schema::dropIfExists
函数中
down
的使用。对于此特定迁移,如果
Schema::create
调用失败,则不会创建表。这意味着没有可删除的表。
dropIfExists
可防止在这种情况下出现额外错误。


3
投票

一种解决方案是从事务内执行迁移,这样,如果迁移过程中发生错误,则不会提交任何内容,并且整个事务会回滚。 Antonio Carlos Ribeiro 编写了一个很棒的类,可以巧妙地处理这个过程;请参阅此处对过程的描述以及GitHub 上已完成的迁移类

安装完他的类后,创建迁移,以便它们扩展新的 Migration 类,并调用

migrateUp()
migrateDown()
代替
up()
down()

class CreateAccountsTable extends PragmaRX\Support\Migration {

    protected function migrateUp()
    ...

...并且无需再次手动修复失败的迁移!


1
投票

7年后,同样的问题仍然发生在我身上,我没有找到官方解决方案,但由于这个问题是相关的并且出现在谷歌上的第一个位置,所以我从我的解决方案开始。可以按如下方式解决:在“up”函数中放置一个try/catch,并在将迁移保存到数据库之前使用“dd”停止该过程。

    public function up()
    {
        try {
            if (!Schema::hasTable('smart_banners_sellers')) {
                Schema::create('smart_banners_sellers', function (Blueprint $table) {
                    $table->increments('id');
                    $table->string('seller_id', 20);
                    $table->bigInteger('smart_banner_id')->unsigned();
    
                    $table->foreign('seller_id')
                        ->references('CodigoVendedor')
                        ->on('vendedores')
                        ->onDelete('Cascade');
    
                    $table->timestamps();
                });
            }
        } catch (Throwable $ex) {
            Schema::dropIfExists('smart_banners_sellers');
            dd("--- ERROR: ". $ex->getMessage());
        }
    }

我使用了“dd”(dump() + die()),因为即使它发现错误并输入“catch”,迁移也会从“migrations”表中注册,下次会被忽略,这不是我们想要什么,因为有错误。我们想要的是:

  • 看到错误,所以我使用了“catch”,
  • 删除创建的表,因为它包含错误,所以我使用“Schema::dropIfExists”
  • 不要在数据库中注册迁移,停止代码执行,这就是我使用“dd”的原因。
© www.soinside.com 2019 - 2024. All rights reserved.