Las migraciones de Laravel son una buena manera de deshabilitar las comprobaciones de claves externas

5 minutos de lectura

avatar de usuario
pavlin

Cuando ejecuto migraciones de laravel, me enfrento a un pequeño inconveniente. Yo uso Laravel 5.1.

Dado que hay muchas tablas con muchas relaciones, probablemente sea imposible cambiar el nombre de los archivos de migración para que se ejecuten en el orden correcto, de modo que no se viole ninguna restricción de clave externa. Esto fue lo que hice una vez en el pasado, y fue muy poco práctico.

Lo que estoy haciendo ahora es definir cada migración de esta manera:

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;');
    }
}

El problema con esto es que es tedioso de escribir y abarrota el código.

También pensé en crear dos archivos de migración ficticios, cuyo único propósito sería habilitar y deshabilitar las comprobaciones de clave externa, y los nombraría de tal manera que se ejecutaran al principio y al final de cada migración.

Si hay una solución elegante, ¿sería posible aplicarla también al proceso de siembra, ya que esto también suele ser un problema allí?

Obviamente, esta es una solución muy improvisada, y estoy preguntando si hay una mejor manera de hacerlo. Hay alguna beforeMigrate y afterMigrate métodos que puedo anular o algo por el estilo?

Y si no, ¿cómo haces para hacerlo?

Cualquier idea sería apreciada, no me gustan todas las opciones que he indicado.

  • Siempre deben ejecutarse en el orden correcto si usó crafty para crear las migraciones y tienen las marcas de tiempo correctas. Se ejecutan en el orden de las marcas de tiempo.

    – Carlos

    15 de diciembre de 2015 a las 23:36

  • Sí, eso es lo que quise decir cuando me referí al orden del archivo. El problema es que a veces no las genera en el orden correcto, oa veces puede tener una dependencia cíclica, es decir, ambas tablas tienen claves externas que se refieren entre sí. No importa en qué orden ejecute ese ejemplo, obtendrá una violación de restricción de clave externa a menos que deshabilite las comprobaciones.

    – pavlin

    16 de diciembre de 2015 a las 7:19

  • La mejor manera de evitar estos problemas es crear primero una migración con creación de tablas y luego otra que cree/elimine claves.

    – Máximo Lanín

    16 de diciembre de 2015 a las 9:45

  • No está particularmente relacionado, pero también puede deshabilitar las comprobaciones de clave externa de esta manera: \DB::getSchemaBuilder()->disableForeignKeyConstraints(). No estoy seguro si hay una mejor manera de llamar a este método.

    – x-yuri

    28 de noviembre de 2016 a las 18:15

  • Tengo exactamente el mismo problema otra vez, con otro proyecto más. Como truco temporal, estoy haciendo exactamente lo que se sugiere en el OP; 2 archivos de migración ficticios con marca de tiempo para ejecutarse antes y después de todo lo demás, desactivando y activando las comprobaciones de clave externa respectivamente. Creo que el sistema de migración de Laravel tiene fallas fundamentales. Revisar el código de creación de tablas dividido entre todos estos archivos es feo e inconveniente. De ahora en adelante, voy a colocar todo en un único archivo de migración, con las comprobaciones de clave externa deshabilitadas y habilitadas al principio y al final, y toda la lógica del esquema se establece claramente en el medio.

    – Íñigo

    8 dic 2016 a las 13:25

avatar de usuario
4niveles

Tuve una tarea similar cuando Lumen / Laravel comenzó a usar Passport y tuve que deshacerme de la implementación anterior del servidor Oauth lucadegasperi/oauth2-servidor-laravel.

Finalmente logré poner las cosas en marcha creando 2 migraciones, donde la primera borra las claves externas y la segunda elimina las tablas.

Tuve que usar fechas anteriores a las migraciones de Pasaporte de Laravel (2016-06-01) por lo que serán ejecutados antes que aquellos.

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();
    }
    //...
}

Y en el segundo archivo
2016_05_31_000001_clear_old_oauth.php

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

  • ¡Esto es realmente agradable! ¿Esto se agregó recientemente o ha estado en Laravel por un tiempo y simplemente no lo noté cuando tuve este problema?

    – pavlin

    30 de enero de 2017 a las 13:54

  • Hola @Pavlin, no sé cuándo se agregó esto en Laravel/Lumen. Tenga en cuenta que deberá buscar los nombres de las restricciones de clave externa en, por ejemplo. PhpMyAdmin (Estructura -> Relaciones)

    – 4niveles

    30/01/2017 a las 14:00


  • Esto se agregó a Laravel desde la versión 5.2.

    – arielcr

    10 mayo 2017 a las 16:25

avatar de usuario
Recep puede

Lo hice extrayendo la lógica de la clave externa en un archivo de migración separado. Esto me ayudó a:

  • Deshabilite las restricciones de clave externa.
  • Descarte de forma segura la base de datos, si existe.

En codigo:

//file: 2017_06_19_230601_fk_postuser_table.php

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

Otro aspecto importante a recordar es colocar PRIMERO la clave externa, luego la columna. Dejar caer la columna primero arroja el error:

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

El orden correcto importa:

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

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad