Este artículo tiene más de 2 años. Si contiene información técnica es posible que no se ajuste a las últimas versiones.

Importa las tablas de tu base de datos a un nuevo proyecto Laravel

Llega un momento en el que decides que esa aplicación web que hiciste desde cero hace años, necesita actualizarse. Es posible que esa aplicación web fuera una de tus primeros trabajos o que se hiciera con algún código heredado y necesitas pasarte a un framework moderno como Laravel. Aunque la concisa documentación de Laravel explica perfectamente bien como crear tu proyecto desde cero, aquí contaré como importar las tablas de tu aplicación web a un nuevo proyecto en Laravel.

¿Qué necesitaremos para importar nuestro antiguo proyecto?

  1. Crear la estructura apropiada en nuestro proyecto Laravel.
  2. Exportar los datos de nuestras tablas a un formato genérico (JSON).
  3. Importar estos datos en la estructura de Laravel.

Si vienes de un proyecto hecho desde cero o de algo como WordPress primero necesitas tener un conocimiento básico de cómo se afronta la gestión de los datos. Mucho tutorial que se publica asume, no sin cierta lógica, que el lector tiene un conocimiento avanzado sobre muchos conceptos. Esto dificulta la comprensión de los básicos que debes conocer para trabajar con un framework (una base de código preparada para realizar acciones comunes en una aplicación).

La importancia de comprender la organización del patrón MVC

Laravel es un framework PHP basado en un patrón MVC. Esto significa que hay una estricta separación de responsabilidades entre lo que son los datos de nuestra aplicación (Modelo), la lógica en su acceso (Controlador) y su representación final por parte de los usuarios (Vista).

En este caso nos interesa exclusivamente la parte de Modelo, esto es, lo relacionado con la estructura de datos. Trabajar con Laravel es, a fin de cuentas, contar con una formidable abstracción de clases y una implementación determinada (un framework), para gestionar los datos, la lógica y la presentación de nuestra aplicación.

Una vez tenemos en cuenta estas cuestiones, la estructura que necesitamos tener en nuestro proyecto Laravel incluye:

  • Modelo. Es una clase que representa una entidad (ej. clientes, facturas, pedidos) y que forma la estructura básica para gestionar los datos dentro de nuestra aplicación.
  • Migración. Es una clase que nos permite definir el esquema y la definición del tipo de datos de nuestras tablas y también sirve como un control de versiones de nuestra base de datos. Lo más cercano al gestor de una tabla de nuestra base de datos.
  • Seeder. Es un generador que permite la introducción (siembra) de datos desde una fuente de datos o un sistema de generación de datos de prueba (para hacer pruebas de nuestra aplicación).

Es interesante comprender que ninguna de las anteriores hace referencia directamente a las tablas de la base de datos. Un concepto básico en Laravel es que el Modelo interactúa con los datos, aislando por completo al desarrollador de acceder a las tripas de su base de datos. De hecho un punto fundamental a la hora de comprender este tipo de framework, es el concepto del mapeo objeto-relación (ORM). Básicamente consiste en una especie de base de datos basada en objetos, donde las clases hacen referencia a las entidades de la base de datos. ¿Y por qué es útil? Para responder seguimos usando la palabra abstracción. Laravel destaca por su lenguaje sintético, claro y vaya, elocuente. No es de extrañar que su ORM se llame Eloquent (saben poner nombres) y permite una interfaz completísima para trabajar con tus datos.

En Laravel la creación de las clases se realiza a través de Artisan, una herramienta de línea de comandos que te abstrae de codificar a mano las clases y facilita muchísimo la administración de la aplicación. Se podría decir que un gran framework tiene ese toque de magia, donde un comando interpreta por detrás la complejidad de producir código que exige un conocimiento profundo de la aplicación.

Para ilustrar con un ejemplo la importación de la base de datos al proyecto Laravel, partiré de dos sencillas tablas: contactos y procedencias de los contactos. Es un proyecto recién instalado, con lo que asumiremos que tienes una instalación fresca de Laravel en tu máquina. En mi caso estoy usando la versión 7.

1. Crear la estructura apropiada en nuestro proyecto Laravel

Es habitual que una tabla de la base de datos se suela corresponder a un Modelo de nuestra aplicación. Por tanto empezaremos por definir nada más que nuestros dos modelos. Para ello vamos a la línea de comandos en nuestro carpeta del proyecto Laravel y ejecutamos el siguiente comando:

php artisan make:model Contact

Esto nos creará en la carpeta app/ el archivo Contact.php que contendrá la definición básica de la clase Modelo. Para evitar problemas en la importación creamos una variable $guarded con un array vacío para indicar que todas las propiedades serán asignables de manera masiva. Por lo general es común lo contrario, definir una variable array $fillable que incluye las columnas de la tabla que se pueden asignar, protegiendo el resto de escrituras. Usar un framework también es hacer este tipo de contratos con la lógica de tu aplicación. Usas atributos y métodos conforme a lo establecido en este caso por Laravel. Tras ejecutar el comando anterior obtenemos en la ubicación /app/Contact.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
  protected $guarded = [];
}

¿Genial no?. Un simple comando hace el trabajo por nosotros y tenemos la clase Contact preparada para ser usada en nuestra aplicación. Ahora haremos lo propio con nuestra otra tabla procedencias que llamaré “Origins” (el uso del inglés para las entidades será habitual) Por tanto, lo siguiente es crear el modelo Origin agregando la misma variable $guarded descrita en el punto anterior.

php artisan make:model Origin

Por tanto, en la ubicación /app/Origin.php obtendremos el código.

namespace App;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
  protected $guarded = [];
}

Estupendo, tenemos dos modelos recién creados y lo siguiente es preparar las migraciones. Por ahora una migración representará la definición o esquema de cada tabla. Como es un proyecto antiguo, lo lógico es que pensemos en mantener la estructura o pensar en definir una diferente. Por tanto, es un buen momento para optimizar nuestras tablas. Considera si la estructura responde a la lógica del negocio y cumple correctamente con la normalización y el tipo de datos. Además, es muy importante comprender que las migraciones también permiten control de versiones, y por tanto, volver a migraciones anteriores o directamente volver al inicio y empezar de nuevo. Es como un Git para tus tablas.

Para las migraciones me voy a basar casi por completo en las columnas de las tablas que tengo funcionando en la aplicación y mi labor será reproducirlas en Laravel. Para ello volvemos a línea de comandos para que sea Artisan el que haga el trabajo inicial. Empezamos por la tabla de Contactos.

php artisan make:migration create_contacts_table

Este comando crea nuestra migración y para ello le indicamos el nombre que por convención usaremos create_[modelo_plural]_table En esta ocasión Laravel nos escribirá en la carpeta /database/migrations el correspondiente archivo con una nomenclatura especial del tipo:

2020_06_02_152241_create_contacts_table

Esto es, la fecha y hora junto con el nombre exacto de la migración. El factor tiempo es importante debido al orden de ejecución y cómo evaluará Laravel el momento en el que se realiza la migración (versiones). Atención a esto que en ocasiones puede ser motivo de conflicto a la hora de ejecutar código de creación o actualización de tus tablas. El contenido por ahora es:

/database/migrations/2020_06_02_152241_create_contacts_table.php

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

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

    $table->id();
    $table->timestamps();

  });

}

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


}

Ahora mismo nuestra migración está lista para definir las columnas de nuestra tabla, junto con el tipo de datos y alguna característica adicional. Siempre hay que tener a mano la documentación de Laravel, ya que las opciones son muchas y es algo desbordante recordarlas. Para definir las columnas me basaré en la estructura de la tabla Contactos en mi anterior aplicación.

Estructura tabla a exportar en formato JSON

Al final, añadiendo columnas para reproducir la estructura anterior, mi archivo de migración para la tabla de contactos quedará así.

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

class CreateContactsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
  Schema::create('contacts', function (Blueprint $table) {
   $table->integer('id')->unsigned()->primary();
   $table->string('nombre');
   $table->string('apellidos')->nullable();
   $table->string('direccion')->nullable();
   $table->string('poblacion')->nullable();
   $table->string('cp')->nullable();
   $table->string('telefono')->nullable();
   $table->string('movil')->nullable();
   $table->string('email')->nullable();
   $table->string('observaciones')->nullable();
   $table->boolean('seguimiento');
   $table->integer('origin_id')->unsigned();
   $table->foreign('origin_id')->references('id')->on('origins');
   $table->timestamps();

  });
}

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


}

Algunas cosas a destacar del anterior fichero de migración

1) Es fundamental establecer la clave primaria de nuestra tabla. Laravel asume que una columna id, es la candidata para formar una clave primaria y única. No obstante, en mi caso hasta que no he establecido de manera inequívoca esa opción, me ha dado problemas en la importación. Por tanto he indicado que es “un entero de nombre id, de signo positivo y es una clave primaria.”

$table->integer(‘id’)->unsigned()->primary();

2) Muchas columnas están establecidas con la propiedad nullable. En el caso de la importación me parece muy apropiado ya que la anterior tabla contenía muchos campos vacíos y así se indica expresamente esta condición en la nueva tabla.

3) Vamos a establecer una clave foránea creando la columna que va a hacer referencia a la clave primaria de la tabla Origins. Primero creamos la columna y luego establecemos la clave foránea siguiendo la sintaxis de Laravel.

$table->integer(‘origin_id’)->unsigned();
$table->foreign(‘origin_id’)->references(‘id’)->on(‘origins’);

Ahora nos queda la migración de la tabla procedencias (Origins) que en este caso es más sencillo, porque sólo contiene un clave y un nombre.

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

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

$table->integer('id')->unsigned()->primary();
$table->string('name')->unique();
$table->timestamps();
});
}

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


}

Ahora que ya tenemos la estructura de nuestras tablas creadas es momento de sembrarlas con los datos procedentes del proyecto anterior.

2. Exportar los datos de nuestras tablas a un formato genérico (JSON).

Necesitamos obtener un fichero estructurado para realizar la importación. En este caso exportaremos nuestros datos a un fichero JSON desde nuestra base de datos de origen. También se podría hacer desde un fichero CSV, pero habría que aplicar otra solución para leer y recorrer cada fila de la tabla. La ventaja de JSON es que tenemos funciones nativas de PHP que nos permite acceder con sencillez.

Desde nuestro gestor de base de datos buscamos la opción de exportar tabla en forma JSON (por ejemplo en phpMyAdmin)

Opción exportar tabla mysql a formato JSON desde phpMyAdmin

Una vez lo tenemos crearemos una carpeta en nuestro proyecto Laravel para alojar el archivo JSON. Por ejemplo en mi caso la ruta es /database/seeds/json

3. Importar estos datos en la estructura de Laravel.

Finalmente nos queda crear la función de sembrar (seeder) nuestra tabla con el JSON exportado. A tal efecto tendremos que crear el seeder, de nuevo con la ayuda Artisan.

php artisan make:seeder ContactsTableSeeder

Este comando nos generará en database/seeder nuestra estructura base para el fichero seeder. Como se verá, la clase sólo consiste en un método llamado run y que ejecutará la importación de los datos para la siembra.

use Illuminate\Database\Seeder;

class ContactsTableSeeder extends Seeder
{
  /**
  * Run the database seeds.
  *
  * @return void
  */

  public function run()
  {
  //
  }
}

Aquí necesitaremos comparar nuestra estructura anterior para añadir columna a columna los datos en la nueva tabla. El proceso consiste en:

  1. Añadimos el modelo Contact.
  2. Si existe la tabla contacts, la borraremos para evitar inconsistencias con datos anteriores.
  3. Obtenemos el fichero JSON con nuestros datos.
  4. Lo preparemos en un array para recorrer y asignar cada columna a la nueva tabla.
use Illuminate\Database\Seeder;
use App\Contact;

class ContactsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
  DB::table('contacts')->delete();
  $json = File::get(__DIR__ . '/json/contactos.json');
  $data = json_decode($json);
    
  foreach ($data as $item){
    	Contact::create(array(
    	    'id' => $item->idContacto,
            'nombre' => $item->nombreContacto,
            'apellidos' => $item->apellidosContacto,
            'direccion' => $item->direcContacto,
            'poblacion' => $item->poblacionContacto,
            'cp' => $item->cpContacto,
            'telefono' => $item->telefonoContacto,
            'movil' => $item->movilContacto,
            'email' => $item->emailContacto,
            'created_at'  => $item->fechaInContacto,
            'updated_at'  => $item->fechaModifContacto,
            'origin_id'  => $item->origenContacto,
            'observaciones'  => $item->observacionesContacto,
            'seguimiento'  => $item->seguirContacto,
    ));
  
   }
}


}

Es importante comprender que el array que se le pasa al método Contact::create tiene como clave el nombre de la columna de nuestro tabla en Laravel y como valor, el nombre de la columna de nuestro anterior tabla.

Para finalizar tenemos que ejecutar este archivo seeder desde un archivo que controla la ejecución global de los seeders y que se encuentra en /database/seeds/DatabaseSeeder.php y al que tendremos que añadir cada seeder. ¿Cómo? Añadiendo en el método run() una llamada explícita al seeder que hemos creado.

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
  public function run()
  {
  $this->call(ContactsTableSeeder::class);
  }
}

Para ejecutar el seeder tenemos que volver a Artisan y ejecutar el comando final que nos sembrará los datos de todos nuestros seeder.

php artisan db:seed

Tras ejecutar este comando y algunos de los anteriores, es bastante probable que aparezcan fallos relacionados con los datos o la sintaxis. Es cuestión de ir rastreando cualquier problema y corregir hasta que consigamos migrar los datos. Tras tener nuestros datos en el nuevo proyecto Laravel podremos concentrarnos en trabajar nuestra aplicación, incluso es posible que nos sirvan para realizar las primeras pruebas (más fácil de detectar problemas con datos reales).

Consideraciones adicionales

Hay que poner una especial atención a la hora de nombrar las claves de nuestras tablas. Por convención Laravel espera que la columna con clave primaria se nombre como “id” y es importante nombrar a los modelos y las claves con un sistema homogéneo. Tendrás menos problemas a la hora de realizar consultas y será más fácil mantener la aplicación. El proceso de establecer un modelo sólido, definiendo bien el tipo de datos que almacena cada columna y su relación con otras tablas, es fundamental para resolver errores cuando consultes o importes las tablas (por ejemplo de una versión de base de datos a otra).

Lo normal es tener siempre algún error cuando trabajas con los datos. Lo bueno es que la comunidad y la documentación hace un gran trabajo, recopilando errores y ofreciendo soluciones.