Migraciones
¡Hemos creado una clase de entidad! Pero... eso es todo. La tabla correspondiente aún no existe en nuestra base de datos.
Pensemos. En teoría, Doctrine conoce nuestra entidad, todas sus propiedades y sus atributos ORM\Column. Entonces... ¿no debería Doctrine ser capaz de crear esa tabla por nosotros automáticamente? Sí Puede hacerlo.
El comando make:migration
Cuando instalamos Doctrine anteriormente, venía con una biblioteca de migraciones que es increíble. ¡Échale un vistazo! Cada vez que hagas un cambio en la estructura de tu base de datos, como añadir una nueva clase de entidad, o incluso añadir una nueva propiedad a una entidad existente, debes ir a tu terminal y ejecutar
symfony console make:migration
En este caso, estoy ejecutando symfony console porque esto va a hablar con nuestra base de datos. Ejecuta eso y... ¡perfecto! Se ha creado un nuevo archivo en el directorio migrations/con una marca de tiempo para la fecha de hoy. Vamos a comprobarlo! Busca migrations/ y abre el nuevo archivo.
| // ... lines 1 - 12 | |
| final class Version20220718170654 extends AbstractMigration | |
| { | |
| public function getDescription(): string | |
| { | |
| return ''; | |
| } | |
| public function up(Schema $schema): void | |
| { | |
| // this up() migration is auto-generated, please modify it to your needs | |
| $this->addSql('CREATE SEQUENCE vinyl_mix_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); | |
| $this->addSql('CREATE TABLE vinyl_mix (id INT NOT NULL, title VARCHAR(255) NOT NULL, description TEXT DEFAULT NULL, track_count INT NOT NULL, genre VARCHAR(255) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); | |
| $this->addSql('COMMENT ON COLUMN vinyl_mix.created_at IS \'(DC2Type:datetime_immutable)\''); | |
| } | |
| public function down(Schema $schema): void | |
| { | |
| // this down() migration is auto-generated, please modify it to your needs | |
| $this->addSql('CREATE SCHEMA public'); | |
| $this->addSql('DROP SEQUENCE vinyl_mix_id_seq CASCADE'); | |
| $this->addSql('DROP TABLE vinyl_mix'); | |
| } | |
| } |
Esto contiene una clase con los métodos up() y down()... aunque nunca ejecuto las migraciones en sentido "descendente", así que nos centraremos sólo en up(). Y... ¡esto es genial! El comando de migraciones vio nuestra entidad VinylMix, se dio cuenta de que faltaba su tabla en la base de datos y generó el SQL necesario en Postgres para crearla, incluyendo todas las columnas. Ha sido muy fácil.
Ejecutar la migración
Bien... ¿cómo ejecutamos esta migración? De vuelta a tu terminal, ejecuta:
symfony console doctrine:migrations:migrate
Di y para confirmar y... ¡precioso! Nos dice que es Migrating up toesa versión específica. Parece... ¡que ha funcionado! Para asegurarte, puedes probar con otro comando bin/console: symfony console doctrine:query:sql con SELECT * FROM vinyl_mix.
symfony console doctrine:query:sql 'SELECT * FROM vinyl_mix'
Cuando probamos eso... ¡Ups! Perdona mi error tipográfico... aquí no hay nada que ver. Inténtalo de nuevo y... ¡perfecto! ¡No nos da ningún error! Sólo dice queThe query yielded an empty result set. Si esa tabla no existiera, comovinyl_foo, Doctrine nos habría gritado.
Así pues, ¡la migración se ha ejecutado!
Cómo funcionan las migraciones
Este hermoso sistema merece algunas explicaciones. Ejecuta
symfony console doctrine:migrations:migrate
de nuevo. ¡Compruébalo! ¡Es lo suficientemente inteligente como para evitar ejecutar esa migración por segunda vez! Sabe que ya lo hizo. Pero... ¿cómo? Prueba a ejecutar otro comando:
symfony console doctrine:migrations:status
Esto da alguna información general sobre el sistema de migración. La parte más importante está en Storage donde dice Table Name y doctrine_migration_versions.
El asunto es el siguiente: la primera vez que ejecutamos la migración, Doctrine creó esta tabla especial, que almacena literalmente una lista de todas las clases de migración que se han ejecutado. Entonces, cada vez que ejecutamos doctrine:migrations:migrate, busca en nuestro directorio migrations/, encuentra todas las clases, comprueba en la base de datos cuáles no se han ejecutado ya, y sólo llama a esas. Una vez que las nuevas migraciones terminan, las añade como filas a la tabla doctrine_migration_versions.
Puedes visualizar esta tabla ejecutando:
symfony console doctrine:migrations:list
Ve nuestra única migración y sabe que ya la ha ejecutado. ¡Incluso tiene la fecha!
Esto es genial... pero vamos a ir más allá. A continuación, añadamos una nueva propiedad a nuestra entidad y generemos una segunda migración para añadir la columna.
15 Comments
Hi. Is there any restriction (with symfony 6) for executing an $entityManager->flush() inside a migration?
With symfony 5.4, I have no errors, but with 6.1 a PDOException is thrown like this:
[PDOException (42000)] <br /> SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT DOCTRINE_9 does not existHey AngelFQC,
Hm, probably it might be not a good idea to call flush manually. Did you try to leverage preUp()/postUp()? Though I'm not sure if that will cause different transactions, never did that before. Btw, how do you get access to the EntityManager inside your migration? IIRC that's not that easy by default, most probably for that exact reason to avoid intrusion into internal processes.
Btw, did you think about "migration command" instead? That might be a good idea for complex cases when you need to migrate some data but also executing some PHP logic for that. So, with migration you just prepare the DB structure, and then run a migration command which is a Symfony command you wrote that will migration the actual data and fill the new columns base on specific conditions. And there you're safe to do intermediate flush()
Cheers!
Cheers!
Hi, I have problem with symfony console
make:migrationon MacOSIt says this error:
Hey Vojtech,
Make sure your DB server up and running, it seems like you're not using a standard port, i.e. 49959 instead of 3306 for mysql - so please, make sure your DB credentials are correct.
Also, if you're using Docker don't forget to run the command via
symfony console make:migrationinstead ofbin/console make:migrationotherwise your CLI will not be able to connect to the DB,symfonycommand will handle it well for you.I hope that helps!
Cheers!
If I try symfony console make:migration it gives an error that the metadata storage is not up to date, please run the sync-metadata-storage command to fix this issue.
Running that command does nothing.
I know the database works, because I already have it set up for accepting a login/registration, so I know the database connection URL etc works. that it accepts the user entity and all the fields connected.
The fun thing is, when I make a new database (named mixed_vinyls and change the database URL to reflect this), without any previous entities linked to it, it does work, it does migrate. But that's not particularly useful.
The web, stackoverflow, github etc, seem convinced this is a metadata / .env issue, but that can't be as the database does work.
Edit to add,
1) created a new database as mentioned, it does not migrate the VinylMix entity but it does migrate the User entity I have for the login stuff.
2) going back to a prior github version (I push after every video) and using the old (mixed_vinyl) database gives the same error message of metadata storage is not up to date.
Edit again:
It's fixed.
I needed to add mariadb-10.5.2 to my database URL string, which wasn't necesary when I made the first migration with the user entity....
Programming is weird....
Hey Roy!
Yup :p.
For reference about this mystery, if you had run
bin/console doctrine:migrations:sync-metadata-storagethat might have done it (the command incorrectly says to usesync-metadata-storagefor confusing reasons that aren't important). I believe this is caused when you get a new version of doctrine migrations and they've changed how the store the migrations in the database. Running this command will "alter" the table to match whatever the new structure is.Cheers!
Learning this for my first internship after my education only teaches minimal and barebones HTML/CSS/PHP etc....
Decided to buy these courses and it's teaching me more already than school did in 3.5 years! Just... I have so many questions that sort of get answered. Thanks for all the replies!
Ah, I'm so thrilled!!! Keep up the good work - happy to answer any questions along the way :)
Heh, I wondered why the reply after 4 days...
Things so far work fine. Though I do seem to be missing somethings here and there. Have you, in any episode, handled how we can make the form push to the database? As mentioned before, school done very minimal html / css, never even explained frameworks existed... So after being hyped up that I was ready for an internship I now face the reality that, well, I clearly am not! ha!
Anyways, thanks again for all the support and episodes!
Hey @Roy!
Do you mean, submitting a form, taking that data, and finally saving it to the database? Check out out https://symfonycasts.com/screencast/symfony-forms tutorial if you haven't yet. It is actually built on an old version of Symfony (we'll update it next year), but that's because the form system hasn't changed much :).
The real world of web dev is complex! But you're doing the right stuff :)
I want to ask if it's at all possible to work with an existing database, for example xampp db. I have been searching for the answer for a few days and no one seems to know how to do that.
Hey Erik!
Absolutely :). And it's super easy:
1) Don't bother starting Docker - you just don't need this.
2) Look inside
.envfor theDATABASE_URLenvironment variable. Copy whatever one (e.g. postgres, mysql) that matches your setup.3) Create a
.env.localfile, paste thatDATABASE_URLinside, then customize it for your real database name, username, password, etcThat's it! You could also modify
.envdirectly... the downside being that your local machine database credentials would get committed to the repository. But.env.localis ignored from git.Let me know if that helps!
Hi there,
This line does not work as such : symfony console doctrine:query:sql 'SELECT * FROM vinyl_mix'
Too many arguments to "doctrine:query:sql" command, expected arguments "sql".
Benoit
Hey Benoit,
It seems like your terminal does not like single quotes
'try surrounding your SQL code with double quotes"SELECT * FROM vinyl_mixCheers!
"Houston: no signs of life"
Start the conversation!