I've found that, while building up my database schema in Laravel, that failed migrations don't rollback, which kind of makes migrations pointless.
For example, I have this migration:
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');
});
Anyway, if I run that migration, the table gets created just fine, but the foreign key fails and I get an error. That's fine. I should get an error. BUT, common sense has me assuming the following:
Okay, so
Am I doing something wrong here? The only way I've figured out how to "undo" a failed migration is to actually go into the database and drop the table. This is extremely frustrating when working on a complex schema where I'm going back and forth fixing errors.
So, I guess now that I've had my little rant, my question is:
How do I rollback a migration that throws an error?
Coming back to this question years later and knowing much more about how databases work now:
There is no way for a migration to "automatically rollback". Schema changes cannot be performed in transactions. Only INSERTS, UPDATES, and DELETES can be rolled back.
In order for this to function correctly, there would need to be an equivalent "drop" script that runs in the event of an error to revert the schema change.
EDIT: It seems like this question still gets some traction, so I wanted to link Larvel's documentation on how to structure a migration file: https://laravel.com/docs/10.x/migrations#migration-structure, and I'll post the code here for posterity, along with an adjustment on how to handle errors:
<?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');
}
};
So, the down
function will execute if you choose to manually rollback a migration or an error occurs.
The final thing to note is the use of Schema::dropIfExists
in the down
function. For this particular migration, if the Schema::create
call fails, then no table is created. That means there is no table to drop. dropIfExists
prevents an additional error in this situation.