我发现,在 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');
});
无论如何,如果我运行该迁移,表创建得很好,但外键失败并且出现错误。没关系。我应该得到一个错误。但是,常识让我做出以下假设:
好吧,所以
我在这里做错了什么吗?我想出如何“撤消”失败的迁移的唯一方法是实际进入数据库并删除表。当我处理复杂的模式时,我要来回修复错误,这是非常令人沮丧的。
所以,我想现在我已经有了我的小咆哮,我的问题是:
如何回滚引发错误的迁移?
多年后回到这个问题,现在对数据库的工作原理有了更多了解:
迁移无法“自动回滚”。无法在事务中执行架构更改。仅 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
可防止在这种情况下出现额外错误。
一种解决方案是从事务内执行迁移,这样,如果迁移过程中发生错误,则不会提交任何内容,并且整个事务会回滚。 Antonio Carlos Ribeiro 编写了一个很棒的类,可以巧妙地处理这个过程;请参阅此处对过程的描述以及GitHub 上已完成的迁移类。
安装完他的类后,创建迁移,以便它们扩展新的 Migration 类,并调用
migrateUp()
和 migrateDown()
代替 up()
和 down()
:
class CreateAccountsTable extends PragmaRX\Support\Migration {
protected function migrateUp()
...
...并且无需再次手动修复失败的迁移!
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”表中注册,下次会被忽略,这不是我们想要什么,因为有错误。我们想要的是: