Search code examples
databaselaravel-4

Laravel Migrations that cause error don't rollback


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:

  1. Since the migration failed, the changes should automatically rollback. Well, they don't.

Okay, so

  1. I should call migrate:rollback to undo those changes. Well, there is no record of that migration happening so I end up rolling back the one that occurred before.

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?


Solution

  • 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.