Home » Php » php – Laravel migrations nice way of disabling foreign key checks

php – Laravel migrations nice way of disabling foreign key checks

Posted by: admin April 23, 2020 Leave a comment

Questions:

When running laravel migrations, I am facing a small inconvenience. I use Laravel 5.1.

Since there are a lot of tables with many relationships, it is probably impossible that I rename the migration files so they run in the correct order, so no foreign key constraint is violated. This was what I did once in the past, and it was very inpractical.

What I’m doing now is defining each migration like this:

class CreateSomeTable extends Migration
{
    public function up()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
        // my table definitions go here
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
    }

    public function down()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
        // drop table
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
    }
}

The problem with this is that it’s tedious to write and it clutters up the code.

I’ve also thought about creating two dummy migration files, whose only purpose would be to enable and disable the foreign key checks, and I would name them in such a way that they would run at the beginning and the end of each migration.

If there is an elegant solution, would it be possible to apply it to the seeding process as well, since this tends to be a problem there as well.

This is obviously a very improvised solution, and I am asking if there is a better way of doing it. Is there some beforeMigrate and afterMigrate methods that I can override or something along those lines?

And if not, how do you go about doing it?

Any insights would be appreciated, I dislike all the options I have stated.

How to&Answers:

I had a similar task at hand when Lumen / Laravel started using Passport and I had to ditch the previous oauth server implementation from lucadegasperi/oauth2-server-laravel.

I finally managed to get things going by creating 2 migrations, where the first one clears foreign keys and the second one actually deletes the tables.

I had to use dates before the migrations of Laravel’s Passport (2016-06-01) so they will be executed before those.

2016_05_31_000000_clear_old_oauth_relations.php

//...
class ClearOldOauthRelations extends Migration
{
    public function up()
    {
        Schema::disableForeignKeyConstraints();
        // drop foreign keys
        Schema::table('oauth_access_tokens', function (BluePrint $table) {
            $table->dropForeign('oauth_access_tokens_session_id_foreign');
        });
        //...
        Schema::enableForeignKeyConstraints();
    }
    //...
}

And in the second file
2016_05_31_000001_clear_old_oauth.php

//...
public function up()
{
    Schema::disableForeignKeyConstraints();
    Schema::drop('oauth_access_tokens');
    //...
    Schema::enableForeignKeyConstraints();
}
//...

Answer:

I got this done by extracting the foreign key logic into a separate migration file. This helped me to:

  • Disable the foreign key constraints.
  • Securely drop the database, if it exists.

In code:

//file: 2017_06_19_230601_fk_postuser_table.php

public function down()
{
        Schema::disableForeignKeyConstraints();
        Schema::dropIfExists('post_user');
}

Answer:

Another important aspect to remember is to drop the foreignKey FIRST, then the column. Dropping the column first throws the error:

Cannot drop index 'tableName_columnName_foreign': needed in a foreign key constraint

The proper order matters:

    public function down()
    {
        Schema::table('tableName', function (Blueprint $table) {
            $table->dropForeign(['columnName']); // fk first

            $table->dropColumn('columnName'); // then column
        });
    }