Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

joshsalway's avatar

How do I drop Foreign Key constraints with Migrations?

How do I drop Foreign Key constraints with Migrations?

Laravel Docs says (https://laravel.com/docs/6.x/migrations#foreign-key-constraints): To drop a foreign key, you may use the dropForeign method. Foreign key constraints use the same naming convention as indexes. So, we will concatenate the table name and the columns in the constraint then suffix the name with "_foreign":

$table->dropForeign('posts_user_id_foreign');

Or, you may pass an array value which will automatically use the conventional constraint name when dropping:

$table->dropForeign(['user_id']);

However, It doesn't drop the foreign key constraint. I need to do it in combination to remove the FK constraint and then the column, like so:

Schema::table('posts', function (Blueprint $table) {
      $table->dropForeign('posts_user_id_foreign');
      $table->dropColumn('user_id');
});

Once I removed the foreign key and column, I was able to re-add the same column without the Foreign Key constraint and in the same correct order using the ->after('column') modifier, like so:

Edit:

    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->bigInteger('user_id')->after('id')->nullable();
        });
    }
0 likes
14 replies
bobbybouwmann's avatar

Well, dropping should be very easy

// Create foreign key with Laravel convention
$table->foreign('user_id')->references('id')->on('users');

// Drop foreign key with Laravel convention
$table->dropForeign(['user_id']);

Alternatively you can assign a different foreign key name and drop it by that, like you showed in your example. But if you stick with the Laravel convention, this should work out of the box.

4 likes
stefanp's avatar

I have a similar situation where I want to remove just the key constrain not the column and not working. I do not know why, maybe you can help me.

Take a look at my code:

--migration for creating the table--

class CreateUnitRoomTypePricesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {

        Schema::create('unit_room_type_prices', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('unit_room_type_id');
            $table->unsignedBigInteger('unit_price_id');
            $table->boolean('front')->default(false);
            $table->decimal('price_general',10,0)->nullable();
            $table->decimal('price_weekdays',10,0)->nullable();
            $table->decimal('price_weekend',10,0)->nullable();

            $table->timestamps();
        });

        Schema::table('unit_room_type_prices', function (Blueprint $table) {
            $table->foreign('unit_room_type_id')->references('id')->on('unit_room_types');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('unit_room_type_prices');
    }
}

--migration for removing the key constrain--

class DropForeignKeyOnUnitRoomTypePrices extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('unit_room_type_prices', function (Blueprint $table) {
            $table->dropForeign(['unit_room_type_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
    }
}

--artisna migrate output--

root@982053685c3f:/var/www/html# php artisan migrate --path=/database/migrations/2022_09_28_141435_drop_foreign_key_on_unit_room_type_prices.php
Migrating: 2022_09_28_141435_drop_foreign_key_on_unit_room_type_prices

   Illuminate\Database\QueryException

  SQLSTATE[42000]: Syntax error or access violation: 1091 Can't DROP 'unit_room_type_prices_unit_room_type_id_foreign'; check that column/key exists (SQL: alter table `unit_room_type_prices` drop foreign key `unit_room_type_prices_unit_room_type_id_foreign`)

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671
    667|         // If an exception occurs when attempting to run a query, we'll format the error
    668|         // message to include the bindings with SQL, which will make this exception a
    669|         // lot more helpful to the developer instead of just the database's errors.
    670|         catch (Exception $e) {
  > 671|             throw new QueryException(
    672|                 $query, $this->prepareBindings($bindings), $e
    673|             );
    674|         }
    675|

      +12 vendor frames
  13  database/migrations/2022_09_28_141435_drop_foreign_key_on_unit_room_type_prices.php:18
      Illuminate\Support\Facades\Facade::__callStatic()

      +21 vendor frames
  35  artisan:37
      Illuminate\Foundation\Console\Kernel::handle()
root@982053685c3f:/var/www/html#
szajens's avatar

Laravel has a bug. Currently artisan does not display an error. But still doesn't delete the foreign key.

Someone could report it.

szajens's avatar

@Sinnbeck :D - I'd be chaotic, my English is little. I just started learning Laravel. I can't explain it. I don't know where report.

Sinnbeck's avatar

@szajens maybe make a fresh thread where you explain what the bug is and how we can recreate it?

szajens's avatar

First migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('pamenuitems', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('pamenugroups_id');
            $table->string('name', 20);
            $table->string('icon', 20);
            $table->string('route', 50);
            $table->boolean('active');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('pamenuitems');
    }
};

Second migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('pamenugroups', function (Blueprint $table) {
            $table->id();
            $table->string('name', 20)->unique();
            $table->string('icon', 20);
            $table->boolean('active');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('pamenugroups');
    }
};

I'm execute command:

php artisan migrate

generate last migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('pamenuitems', function (Blueprint $table) {


            $table->foreign('pamenugroups_id')->references('id')->on('pamenugroups');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('pamenuitems', function (Blueprint $table){

            $table->dropForeign('pamenuitems_pamenugroups_id_foreign');
        });
    }
};

I'm execute command:

php artisan migrate
php artisan migrate:rollback

terminal output:

PS D:\web\laravel\budowa-admin> php artisan migrate


  2022_11_11_195257_create_pamenuitems_table ............................................................................................ 29ms DONE  
  2022_11_11_195317_create_pamenugroups_table ........................................................................................... 52ms DONE  

PS D:\web\laravel\budowa-admin> php artisan migrate

   INFO  Running migrations.

  2022_11_11_211919_add_relation_pamenu ................................................................................................ 109ms DONE  

PS D:\web\laravel\budowa-admin> php artisan migrate:rollback

   INFO  Rolling back migrations.

  2022_11_11_211919_add_relation_pamenu ................................................................................................. 26ms DONE

but the foreign key is not removed from the database

Sinnbeck's avatar

@szajens trying to remove a none existing index in mysql does not give any errors, so therefor laravel won't either

2 likes
szajens's avatar

Dropping Foreign Keys To drop a foreign key, you may use the dropForeign method, passing the name of the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes. In other words, the foreign key constraint name is based on the name of the table and the columns in the constraint, followed by a "_foreign" suffix:

$table->dropForeign('posts_user_id_foreign');

does not work, does not remove the key

Edit: Wrong You misinterpreted my code, I'm rollback only last migration

Sinnbeck's avatar

@szajens I get that part. And I just tested it out and it deletes the foreign key as expected. It does however not delete the index, and perhaps that is what is confusing or ?

1 like
landau351's avatar

The way I dropForeign() in a migration is to wrap it in a try/catch;

DB::statement('SET FOREIGN_KEY_CHECKS = 0;');
try {
	Schema::table('checks', function (Blueprint $table) {
		$table->dropForeign('landlord_id');
	});
} catch (Exception $e) {
	//
}
DB::statement('SET FOREIGN_KEY_CHECKS = 1;');
jesseyvanofferen's avatar

@landau351 2 tips:

  1. The usage of SET FOREIGN_KEY_CHECKS in this case is not needed. You would only use this is extreme rare cases. If you "Need" this, it very likely means you are not setting up your foreign-keys correctly.

  2. The try-catch would be a bad-practice in this case. If there is any error with your migration, you're not going to be aware of this. You're now running silent-exceptions. For example if you have a typo in your migration, you wouldn't get an exception telling you that your migration won't run.

Please or to participate in this conversation.