Link Search Menu Expand Document

5.10. Migraciones

Las migraciones constituyen una especie de control de versiones para la base de datos de la aplicación. Permiten crear y modificar tablas de la BD con independencia del SGBD que estemos usando.

Con las migraciones no solo podrás reconstruir la base de datos en menos de lo que tarda en decirse “Von Neumann” (algo muy práctico cuando estás en fase de desarrollo), sino que podrás parchear la base de datos de una aplicación en producción en un tiempo record y con riesgo cero.

(Solo el que ha tenido que parchear la base de datos de una aplicación en producción antes de la existencia de las migraciones sabe la tranquilidad de espíritu que esto produce y la cantidad de problemas embarazosos que te quita de encima).

Antes de empezar, recuerda que, para que cualquier operación sobre la base de datos funcione, debes tener bien configuradas estas variables de entorno del archivo .env de Laravel:

DB_HOST=localhost
DB_DATABASE=mi-base-de-datos
DB_USERNAME=mi-usuario-de-BD
DB_PASSWORD=mi-password-de-BD

5.10.1. Crear tablas mediante las migraciones

La primera cosa para las que sirven las migraciones es para crear las tablas de tu aplicación. Olvídate de exportar e importar a mano el archivo SQL de tu base de datos. Eso lo hacían los cromañones.

Vamos a trabajar con un ejemplo, como siempre. Imagina que tenemos una tabla llamada Clients con los campos típicos: id, name, address, etc.

De acuerdo. Creemos, paso a paso, las migraciones de esta tabla:

Paso 1: Inicializar el sistema de migraciones de Laravel (si ya lo hemos hecho antes, nos dará un error al intentar hacerlo otra vez):

$ php artisan migrate:install

Paso 2: Crear la migración para la tabla Clients:

$ php artisan make:migration create_clients_table

Esto generará un fichero en la carpeta /database/migrations cuyo nombre contendrá un timestamp o marca de tiempo. Vamos, que contendrá un numerajo con la fecha y hora actual. Algo tan bonito como /database/migrations/20221226072434createclientstable.php

Si editas ese fichero, verás dos métodos:

  • up() → se ejecuta cuando se lanza la migración. Se encarga de crear la tabla en la base de datos.
  • down() → se ejecuta cuando se cancela la migración. Se encarga de eliminar la tabla de la base de datos.

Paso 3: Editar el fichero /database/migrations/20221226072434createclientstable.php:

En el método up() tienes que indicar las columnas que tendrá la tabla. Por ejemplo:

public function up() {
    Schema::create('clients', function (Blueprint $table) {
       $table->bigIncrements('id')->index();// UNSIGNED BIGINT AUTOINC.
       $table->string('name',75)->unique(); // VARCHAR
       $table->text('address')->nullable(); // TEXT
       $table->integer('level');            // INT
       $table->date('brith_date');          // DATE
       // La siguiente línea crea campos created_at y updated_at. Si la borras
       // esos campos no existirán en tu tabla
       $table->timestamps();
    });
}

public function down() {
    Schema::drop('clients');
}

Paso 4: Lanzar las migraciones.

$ php artisan migrate

Esto creará las tablas que no se hayan creado aún. Es decir, si una migración ya se ha lanzado con anterioridad, no se vuelve a ejecutar para no perder los datos que pudieran existir en esas tablas.

Paso 5: Revertir las migraciones (si es necesario)

Si necesitas revertir la creación de todas las tablas:

$ php artisan migrate:rollback

Para revertir solo el último paso en la creación de tablas:

$ php artisan migrate:rollback --step=1

Para dejar la BD a su estado original (vacía):

$ php artisan migrate:reset

¡Cuidado! Estas acciones son destructivas. Pero, por supuesto, hay una forma de modificar una tabla sin borrarla y volver a crearla. Es lo que vamos a hacer a continuación.

5.10.2. Modificar tablas mediante migraciones

Si necesitas modificar una tabla que ya existe (por ejemplo, para añadir o eliminar campos), tienes dos opciones:

  1. Modificar la migración original (en la que se crea la tabla) para añadir o eliminar el campo afectado. Esto te obligará a lanzar la migración de nuevo y, por lo tanto, la tabla se reconstruirá y todos los datos que pudiera contener se perderán.
  2. Crear una nueva migración en la que únicamente se haga la modificación de la tabla, sin tocar el resto. Esto respetará los datos que la tabla ya pudiera contener.

Como es lógico, la opción 2 será la que preferiremos si la aplicación ya está en producción y necesitamos modificar la estructura de la base de datos. En cambio, durante el desarrollo, puede ser más simple utilizar la opción 1.

Supongamos que queremos añadir un campo email a la tabla Clients del apartado anterior. Si optas por la opción 2, es decir, por crear una nueva migración que se encargue de hacer esa modificación en la tabla sin alterar sus datos, la forma de proceder es la siguiente:

Paso 1. Crear la migración:

$ php artisan make:migration add_email_to_clients --table=clients

(Nota: puedes asignar el nombre que quieras a las migraciones, pero Laravel aconseja utilizar las convenciones que ves en estos ejemplos para simplificarnos la vida)

Paso 2: Editar la migración /database/migration/add_email_to_clients.php para añadir, en el método up(), el campo nuevo; y, en el método down(), especificaremos qué hay que hacer en caso de que se fuerce un rollback de esta migración:

    public function up() {
        Schema::table('clients', function (Blueprint $table) {
            $table->string('email')->after('address');
        });
    }

    public function down() {
        Schema::table('clients', function (Blueprint $table) {
            $table->dropColumn('email');
        });
    }

5.10.3. Otras operaciones en las migraciones

Las migraciones pueden usarse para cualquier otra operación sobre la estructura de la base de datos, como:

  • Cambiar tipos de columnas.
  • Cambiar atributos de columnas (null, unique, default…)
  • Cambiar o asignar claves primarias y ajenas.

Las migraciones construídas de este modo nos permitirán reproducir la base de datos en cualquier servidor o actualizarla en cualquier momento sobre una aplicación en producción sin necesidad de programar parches o exportar la BD a un archivo SQL para importarlo en otro servidor.

Como te dije al principio, solo el que ha hecho alguna vez alguna de estas cosas a mano sabe la cantidad de trabajo que las migraciones ahorran en el mantenimiento de una aplicación y la asombrosa cantidad de errores embarazosos que evita.

Más info en: https://laravel.com/docs/8.x/migrations

5.10.4. Seeding

El seeding es una técnica adicional a la de las migraciones que permite cargar con datos las tablas de la base de datos.

Es muy práctico en estos supuestos:

  • Si quieres tener un conjunto de datos de prueba en tu base de datos de desarrollo, esa que destrozas periódicamente cuando haces pruebas. Con un solo comando, tendrás la base de datos reconstruída después de cada destrozo.
  • Si necesitas cargar algunos datos mínimos en algunas tablas para que la aplicación, una vez desplegada en un servidor de producción, funcione (por ejemplo, para crear un usuario administrador en la tabla Users o para crear algunas entradas en una tabla Options).

Para crear un seeder (por ejemplo, para la tabla users), sigue estos pasos:

Paso 1. Ejecuta el comando:

$ php artisan make:seeder UsersTableSeeder

Paso 2. Edita el archivo /database/seeds/UsersTableSeeder.php y añade algo como esto al método up() (por supuesto, modifica el código para adaptarlo a tu tabla y a tus datos):

    public function run() {
        Users::truncate();  // Opativo: vacía la tabla antes de rellenarla
        DB::table('users')->insert([
            'name' => 'Stephen Falken',
            'address' => 'Oregon 97, Goose Island',
            'email' => 'sfalken@norad.com',
            'brith_date' => '1932-09-03',
        ]);
        DB::table('users')->insert([
            'name' => 'Jennifer Mack',
            'address' => 'Richmond Av 3385, Seattle',
            'email' => 'jenmack876@gmail.com',
            'brith_date' => '1967-01-28',
        ]);        
    }

Paso 3. Ejecuta este comando para lanzar el seeder y que los datos se carguen en tu tabla:

$ php artisan db:seed --class=UsersTableSeeder

Esto cargará solo dos registros en la tabla users. Si quieres más, solo tienes que crear nuevas líneas insert() en el método up().

5.10.4. Automatizar el seeding

Lanzar los seeders de uno en uno puede ser muy tedioso. Puedes lanzar varios seeders con un solo comando si haces lo siguiente:

Paso 1. Edita el fichero /database/seeds/DatabaseSeeder.php

Paso 2. Añade a la función run() una línea como esta por cada seeder que quieras ejecutar automáticamente:

    $this->call(UsersTableSeeder::class); 

Paso 3. ¡Y listo! Al ejecutar el comando db:seed de Artisan, sin indicar la clase, se lanzarán todos los seeders que hayas añadido a run().

$ php artisan db:seed

5.10.5. Lista de comandos superútiles para manejar migraciones

Por último, te muestro un resumen con los comandos más útiles sobre migraciones y seeders para que puedas consultarlos cuando lo necesites:

  • Lanzar todas las migraciones pendientes:

     $ php artisan migrate
    
  • Crear la migración para crear una tabla:

     $ php artisan make:migration <nombre> --create=<tabla>
    
  • Crear una migración para modificar una tabla ya existente:

     $ php artisan make:migration <nombre> --table=<tabla>
    
  • Retroceder un paso en todas las migraciones:

     $ php artisan migrate:rollback
    
  • Retroceder N pasos en todas las migraciones:

     $ php artisan migrate:rollback --step=<N>
    
  • Deshacer todas las migraciones que se hayan ejecutado hasta ahora (resetear la base de datos):

     $ php artisan migrate:reset
    
  • Resetear la base de datos y volver a reconstruirla lanzando todas las migraciones:

     $ php artisan migrate:refresh
    
  • Resetear la base de datos, reconstruirla y llenarla de datos con los seeders:

     $ php artisan migrate:refresh --seed
    
  • Eliminar todas las tablas y lanzar todas las migraciones de nuevo:

     $ php artisan migrate:fresh
    
  • Eliminar todas las tablas y lanzar todas las migraciones y todos los seeders de nuevo:

     $ php artisan migrate:fresh --seed