Symfony: tutorial 4: el día a día con las bases de datos

Symfony4 Doctrine Migrations

Otra vez más traigo de nuevo otro HOWTO, esta vez trabajando día a día con bases de datos en Symfony Flex, Symfony 4. En el tutorial anterior empecé con cómo empezar a crear una base de datos. Ahora lo extiendo ya que trabajar con la base de datos es un tema muy extenso.

En este minitutorial me centro más en cómo modificar la base de datos, cómo es el día a día. Cómo desplegar estos cambios en producción sin temor a dejar sin servicio todo el sistema por haber hecho un cambio inesperado. Es importante que tengas un plan B por si lo último modificado rompe producción, y así puedas hechar atrás los últimos cambios de la base de datos.

Creando un proyecto para hacer pruebas

Doy por sentado de que nos manejamos bien con PHP, Composer y los comandos de Symfony, así que vamos a la línea de comandos y creamos el proyecto con todos los módulos que vamos a usar:

composer create-project symfony/skeleton symfony-tutorial-working-with-databases
cd symfony-tutorial-working-with-databases
composer require maker --dev
composer doctrine server

Con esto ya tenemos algunas cosas básicas, pero vamos a añadir el sistema de plantillas Twig, y algunos extras para hacer automáticamente la interfaz web:

composer require annotations form validator twig-bundle security-csrf

Creando una primera estructura de base de datos

Para jugar con el proyecto y ver todas estas cosas vamos a crear una agenda de direcciones. Así podremos ir haciend modificaciones, guardando datos, etc. Para esto usamos el comando make:entity y creamos una entidad que se llame Dirección con los siguientes campos:

  • Nombre: una cadena de tipo string, que tiene por defecto 255 caracteres.
  • Móvil: también cadena string.
  • Dirección: y de nuevo string.

Para esto ejecutamos:

php bin/console make:entity

..y seguimos las instrucciones. No pongas tildes ni mayúsculas para los nombres de los campos. Con esto se nos creará el fichero de entidad src/Entity/Direccion.php

Creando por primera vez la estructura

Ahora para guardar en la base de datos estos cambios tendremos que modificar el fichero .env para decirle donde tenemos la BD. En mi caso tengo una BD Mysql instalada en local con la siguiente cadena de conexión:

DATABASE_URL=mysql://root@127.0.0.1:3306/test

Y finalmente ejecutaremos por primera vez el creado de la base de datos, y el creado de la estructura de tablas. Para esto tenemos que usar:

php bin/console doctrine:database:create
php bin/console doctrine:schema:create

A lo bruto o sin historial

Ahora viene el día a día, vamos modificando la base de datos y tenemos que pasar estos cambios a producción. Añadimos un par de campos nuevos llamados email y fijo también de tipo string por defecto de 255 caracteres. Así ejecutamos:

php bin/console make:entity Direccion

Ahora tendremos que actualizar la base de datos para que añada estas dos columnas. Antiguamente, a lo bruto, o sin user ningún módulo para llevar historial de modificaciones, podemos ejecutar:

php bin/console doctrine:schema:update --dump-sql

Y veremos qué cambios se necesitan aplicar en la base de datos para que se corresponda con lo que tenemos ya añadido en el PHP de la entidad:

ALTER TABLE direccion ADD email VARCHAR(255) DEFAULT NULL, ADD fijo VARCHAR(255) DEFAULT NULL;

Con esto comenzamos con los posibles problemas. Imaginemos, por ejemplo, que añadimos un campo que debe ser obligatorio, y que referencia a otra tabla. Si ejecutáramos esto en producción, si hacer un recorrido de los datos inicializándolos, tendríamos un serio problema de integridad de datos, ya que quedarían estos sin ser los correctos. Deberíamos de modificar la sentencia SQL anterior, añadirle todo lo necesario, guardarlo en un fichero .sql por si acaso, para posteriormente ejecutarlo en producción y mantener la integridad de los datos.

No es el caso porque simplemente quedarán en blanco las nuevas columnas y no supone un problema. Podemos entonces ejecutar lo siguiente:

php bin/console doctrine:schema:update --dump-sql --force

Con Doctrine Migrations

Doctrine Migrations es un sistema de versionado de la base de datos. Con ésto podremos ir guardando versiones de la base de datos, o mejor dicho, scripts de SQL que hacen que una base de datos pase de una versión a otra. Ya lo teníamos antes disponible como bundle instalable. Ahora es más sencillo ya que viene en el pack al haber instalado Doctrine con Composer.

Con esto podremos preparar estos pasos de versión, editando, revisando estos scripts que pasan la base de datos de una versión a otra. En caso de problemas podremos echar atrás también los cambios en la base de datos, igual que haríamos con Git y los códigos fuentes. Igual que si metemos un error un producción por códigos fuentes podemos echarlo atrás, con Doctrine Migrations también podremos ir versión atrás.

Para guardar la siguiente modificación tenemos el primer comando siguiente:

php bin/console make:migration

..el segundo comando siguiente ejecuta los cambios en la base de datos:

php bin/console doctrine:migrations:migrate

Más abajo explico cómo revertir cambios. Tenemos más comandos, para hacer lo dicho anteriormente, tal y como dice la ayuda de línea de comandos:

  • doctrine:migrations:diff Generate a migration by comparing your current database to your mapping information.
  • doctrine:migrations:execute Execute a single migration version up or down manually.
  • doctrine:migrations:generate Generate a blank migration class.
  • doctrine:migrations:latest Outputs the latest version number.
  • doctrine:migrations:migrate Execute a migration to a specified version or the latest available version.
  • doctrine:migrations:status View the status of a set of migrations.
  • doctrine:migrations:version Manually add and delete migration versions from the version table.

Si nos fijamos en el directorio src/Migrations/ tendremos todos estos scripts. En este caso tendremos sólo el script que añade el email y el fijo a la tabla:

<?php declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20180814194200 extends AbstractMigration
{
    public function up(Schema $schema) : void
    {
        // this up() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('ALTER TABLE direccion ADD email VARCHAR(255) DEFAULT NULL, ADD fijo VARCHAR(255) DEFAULT NULL');
    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('ALTER TABLE direccion DROP email, DROP fijo');
    }
}

Aquí es donde podemos editarlo y añadir más acciones para mantener la integridad de los datos. Quizá hemos añadido un campo que siempre tiene que tener un valor, pero como es nuevo los registros existentes no lo tienen relleno, tendremos que rellenarlos aquí. Quizá es una relación entre entidades, o un largo etéctera.

Terminando, revirtiendo cambios

Fíjate que tenemos dos funciones, una función up que modifica la BD para pasar a la siguiente versión. Y la segunda función down que revierte los cambios. En mi caso, esta modificación de la BD se ha guardado como la versión 20180814194200. Entonces, para lanzar manualmente una migración arriba podemos ejecutar:

php bin/console doctrine:migrations:execute 20180814194200 --up

..y para revertir esta migración:

php bin/console doctrine:migrations:execute 20180814194200 --down
Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

dieciseis − ocho =