Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Database Migrations

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

The Article entity is ready, and Doctrine already knows to save its data to an article table in the database. But... that table doesn't exist yet! So... how can we create it?

Generating a Migration

Ah, this is one of Doctrine's superpowers. Go back to your terminal. At the bottom of the make:entity command, it has a suggestion: run the make:migration command.

I love this! Try it:

php bin/console make:migration

The output says that it created a new src/Migrations/Version* class that we should review. Ok, find your code, open the Migrations directory and, there it is! One migration file:

... lines 1 - 2
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20180413174059 extends AbstractMigration
{
public function up(Schema $schema)
{
// 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('CREATE TABLE article (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, slug VARCHAR(100) NOT NULL, content LONGTEXT DEFAULT NULL, published_at DATETIME DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
}
public function down(Schema $schema)
{
// 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('DROP TABLE article');
}
}

Inside, cool! It holds the MySQL code that we need!

CREATE TABLE article...

This is amazing. No, seriously - it's way more awesome than you might think. The make:migration command actually looked at our database, looked at all of our entity classes - which is just one entity right now - and generated the SQL needed to update the database to match our entities. I'll show you an even better example in a few minutes.

Executing the Migration

This looks good to me, so close it and then go back to your terminal. To execute the migration, run:

php bin/console doctrine:migrations:migrate

This command was also suggested above. Answer yes to run the migrations and... done!

But now, run that same command again:

php bin/console doctrine:migrations:migrate

How Migrations Work

It does nothing! Interesting. Run:

php bin/console doctrine:migrations:status

Ok, this tells us a bit more about how the migration system works. Inside the database, the migration system automatically creates a new table called migration_versions. Then, the first time we ran doctrine:migrations:migrate, it executed the migration, and inserted a new row in that table with that migration's version number, which is the date in the class name. When we ran doctrine:migrations:migrate a second time, it opened the migration class, then looked up that version in the migration_versions table. Because it was already there, it knew that this migration had already been executed and did not try to run it again.

This is brilliant! Whenever we need to make a database change, we follow this simple two-step process: (1) Generate the migration with make:migration and (2) run that migration with doctrine:migrations:migrate. We will commit the migrations to our git repository. Then, on deploy, just make sure to run doctrine:migrations:migrate. The production database will have its own migration_versions table, so this will automatically run all migrations that have not been run yet on production. It's perfect.

Migration a Second Change

To see how nice this is, let's make one more change. Open the Article class. See the slug field?

... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
*/
class Article
{
... lines 12 - 23
/**
* @ORM\Column(type="string", length=100)
*/
private $slug;
... lines 28 - 91
}

This will eventually be used to identify the article in the URL. And so, this must be unique across every article in the table.

To guarantee that this is unique in the database, add unique=true:

... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
*/
class Article
{
... lines 12 - 23
/**
* @ORM\Column(type="string", length=100, unique=true)
*/
private $slug;
... lines 28 - 91
}

This option does only one thing: it tells Doctrine that it should create a unique index in the database for this column.

But of course, the database didn't just magically update to have this index. We need a migration. No problem! Find your terminal and do step 1: run:

php bin/console make:migration

Ha! I even misspelled the command: Symfony figured out what I meant. This created a second migration class: the first creates the table and the second... awesome! It creates the unique index:

... lines 1 - 2
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20180413174154 extends AbstractMigration
{
public function up(Schema $schema)
{
// 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('CREATE UNIQUE INDEX UNIQ_23A0E66989D9B62 ON article (slug)');
}
public function down(Schema $schema)
{
// 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('DROP INDEX UNIQ_23A0E66989D9B62 ON article');
}
}

This is the Doctrine magic I mentioned earlier: the make:migration command looked at the entity, looked at the database, determined the difference between the two, then generated the SQL necessary to update the database.

Now, for step (2), run:

php bin/console doctrine:migrations:migrate

It sees the two migration classes, notices that the first has already been executed, and only runs the second.

Ok! Our database is setup, our Article entity is ready, and we already have a killer migration system. So let's talk about how to save articles to the table.

Leave a comment!

40
Login or Register to join the conversation
Richard Avatar
Richard Avatar Richard | posted 2 years ago

strange. when I create the second migration it also added a line thus:-

$this->addSql('ALTER TABLE article CHANGE published_at published_at DATETIME DEFAULT NULL');

I had changed nothing. Possibly it needed the field in the database to then dertemine it needed to default to null? No idea but I didnt change the definition in the article.php.

1 Reply

Hey Richard!

Sorry for my slow reply. Let's see what's going on here :)

> I had changed nothing. Possibly it needed the field in the database to then dertemine it needed to default to null?

I can at least tell you that this is not the case. It's a good guess - but the answer to this mystery is something else! And, before we debug further, I have a question: what database are you using? Are you possibly using MariaDB? If so, if your server_version config (in config/package/doctrine.yaml) isn't set correctly, then Doctrine gets a bit confused about your database and it will generate this SQL *every* time you run make:migration, even if you run this SQL over and over again.

Let me know if you *are* running MariaDB. And if so, what version you're running. Here is the format you can use to set the server_version setting (or you can also have &serverVersion= on your DATABASE_DSN) if that is the problem.

Cheers!

Reply
Donald Avatar

When I added "unique=true" to the $slug annotation *make:migration* stated that there were no changes detected. I had to run the following before the change was picked up.
./bin/console doctrine:cache:clear-metadata
or
./bin/console cache:clear
also seems to work OK.

1 Reply

Hey Donald,

Thank you for sharing it with others! Yeah, it might be required to clear the cache, but depends on your configuration I think. I'm happy it works for you now.

Cheers!

Reply
Default user avatar
Default user avatar FantomX1 | posted 2 years ago

So basically migrations for doctrine serve just as a speed up process of doctrine update, since the entities changes are already in the version control system. But I assume figuring out each time ORM entities changes compared to database is much more computational consuming especially having it in production, than keeping a migration system and doing heavy comparison calculation at the development environment.

Reply

Hey FantomX1

It goes beyond that. With migrations you can tweak and extend the generated SQL. Also, sometimes you need to populate a field with some computed data. And, if a query goes wrong you can run the down method to revert changes

Cheers!

1 Reply
Default user avatar
Default user avatar Kutay Deril | posted 2 years ago

Please improve ODM support too. Thanks.

Reply

Hey Kutay Deril

Sadly we are not in charge of the Doctrine project but you can send them your feedback: https://www.doctrine-projec...

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | posted 2 years ago

Hello,

In production environment, when I ran:

$ php bin/console make:migration


I got this error:

There are no commands defined in the "make" namespace.


But if I change environment to development, it will run successfully.
So my question is:
does this make:migration will only work in dev environment or am i missing something in prod environment in order to run it successfully.

Best regards,

Reply

Hey Dung,

Sure, migrations is something that is meant to be very useful for production :) But the command you call is not from migration but from MakerBundle ;) And MakerBundle is available in dev/test mode only - you can check the "require-dev" in your composer.json. So, this means that you can *generate* migrations only in dev mode but *run* in dev or prod :)

Does it make sense for you now?

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | victor | posted 2 years ago | edited

Hi victor ,

Thank you for your reply, but I do not really understand your answer. So here is my question again: Can I run "$ php bin/console make:migration" in production environment without error?

Cheers,

Reply

Hey Dung,

Ah, ok, let me explain in a different angle :)

> Can I run "$ php bin/console make:migration" in production environment without error?

I suppose yes, just install MakerBundle in "require" section instead of "require-dev" of your composer.json. I.e. you need to install it with:
$ composer require symfony/maker-bundle

i.e. without --dev at the end of this command. Oh, and don't forget to clear the cache in prod :) *Then* you should be able to use "make:*" commands in production environment.

But the counterquestion to you! *Why* do you need this? :) Generating migrations is something that you would need to do in dev mode only, and then, in prod mode, you will need to *run* those prepared migrations. That's how migrations are meant to be used.

I hope this is clear to you now!

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | victor | posted 2 years ago | edited

uh, i thought i answered you but not sure where it went now. lost my previous response :)

Hi victor for some reason my dev and prod dbases fall out of synch sometime. so I had to run make:migration on both servers. I now understand your answer that I can just run doctrine:migrations:migrate on production. But I need to be able to keep migrations version the same on both servers. This should be easy but not sure why I sometime have out-of-synch migration version issue.

Thanks for your question and concern!

Dung.

Reply

Hey Dung L.

You need to discover why your production database is not in sync to dev (Besides new features adding more tables, schema changes, etc). As Victor said. You create migrations while developing a new feature, commit them to the repository and on deploy, you just execute the migrations. Nothing else should be changing your database schema, otherwise migrations will cause more problems than they solve

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | victor | posted 2 years ago | edited

Ah, I understand now victor. To answer your question, it is because i git ignored the Migrations directory so it will not carry to production, that is why in production I have to run make:migration again.

And the reason I git ignored the Migrations directory is because sometime for whatever reason i dont know yet but my dev and prod databases go out of synch. If I can keep them always in synch then yes I will only need to add Migrations directory back in git and run doctrine:migrations:migrate without having to do make:migration.

Thank you for your comment!

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 3 years ago

Over time a lot of migration files get created. How to handle this?
Just leave 100+ migration files or is it save to delete all and recreate one "huge" migration file?

Reply

Hey Mike,

Good question! You need migrations only for migrating production DB, that’s why you can totally remove them all and create one migration instead as long as you are on the latest migration on production. Just make sure you added this new migration as already executed, otherwise on the next deploy you will execute it and it’s not something you want.

What about me, I don’t think you should have to be able to create the complete DB with migrations, that’s why I’d recommend to just remove them all and that’s it. You have doctrine:database:create and doctrine:scheme:create commands, you can use them locally because probably you don’t care about local dummy data, you can always just re-run fixtures and that’s it.

So, my point is as soon as you executed migrations on production - you can safely remove them because you don’t need them anymore. They did their job. Though, if you have a few stages, like production, beta, etc, where you should care about real data - you should be sure all migrations are executed on all your stages before removing them.

I hope this helps!

Cheers!

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | posted 3 years ago

Hi guys,

I get the following error when I try to make:migration: "An exeption occurred in driver: could not find driver".
How can I fix this?

Best regards,
Dennis

Reply

Hey Dennis E.

Try re-running the command without ending in semicolon (:). If it doesn't fix the problem, please re-run it passing flag -vvv so we can get more debugging info

Cheers!

Reply
Dennis E. Avatar

It's a big text



C:\symfony\tlt>php bin/console make:migration -vvv

In AbstractMySQLDriver.php line 106:

[Doctrine\DBAL\Exception\DriverException]
An exception occurred in driver: could not find driver


Exception trace:
() at C:\symfony\tlt\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\AbstractMySQ
LDriver.php:106
Doctrine\DBAL\Driver\AbstractMySQLDriver->convertException() at C:\symfony\tlt\
vendor\doctrine\dbal\lib\Doctrine\DBAL\DBALException.php:169
Doctrine\DBAL\DBALException::wrapException() at C:\symfony\tlt\vendor\doctrine\
dbal\lib\Doctrine\DBAL\DBALException.php:155
Doctrine\DBAL\DBALException::driverException() at C:\symfony\tlt\vendor\doctrin
e\dbal\lib\Doctrine\DBAL\Driver\PDOMySql\Driver.php:28
Doctrine\DBAL\Driver\PDOMySql\Driver->connect() at C:\symfony\tlt\vendor\doctri
ne\dbal\lib\Doctrine\DBAL\Connection.php:356
Doctrine\DBAL\Connection->connect() at C:\symfony\tlt\vendor\doctrine\dbal\lib\
Doctrine\DBAL\Connection.php:889
Doctrine\DBAL\Connection->executeQuery() at C:\symfony\tlt\vendor\doctrine\dbal
\lib\Doctrine\DBAL\Connection.php:843
Doctrine\DBAL\Connection->fetchAll() at C:\symfony\tlt\vendor\doctrine\dbal\lib
\Doctrine\DBAL\Schema\AbstractSchemaManager.php:206
Doctrine\DBAL\Schema\AbstractSchemaManager->listTableNames() at C:\symfony\tlt\
vendor\doctrine\dbal\lib\Doctrine\DBAL\Schema\AbstractSchemaManager.php:194
Doctrine\DBAL\Schema\AbstractSchemaManager->tablesExist() at C:\symfony\tlt\ven
dor\doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableStatus.php:47
Doctrine\Migrations\Tracking\TableStatus->isCreated() at C:\symfony\tlt\vendor\
doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableManipulator.php:55
Doctrine\Migrations\Tracking\TableManipulator->createMigrationTable() at C:\sym
fony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Configuration\Config
uration.php:317
Doctrine\Migrations\Configuration\Configuration->createMigrationTable() at C:\s
ymfony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Tools\Console\Comm
and\AbstractCommand.php:76
Doctrine\Migrations\Tools\Console\Command\AbstractCommand->initialize() at C:\s
ymfony\tlt\vendor\doctrine\doctrine-migrations-bundle\Command\MigrationsDiffDoct
rineCommand.php:41
Doctrine\Bundle\MigrationsBundle\Command\MigrationsDiffDoctrineCommand->initial
ize() at C:\symfony\tlt\vendor\symfony\console\Command\Command.php:221
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\maker-bundle\src\Maker\MakeMigration.php:79
Symfony\Bundle\MakerBundle\Maker\MakeMigration->generate() at C:\symfony\tlt\ve
ndor\symfony\maker-bundle\src\Command\MakerCommand.php:104
Symfony\Bundle\MakerBundle\Command\MakerCommand->execute() at C:\symfony\tlt\ve
ndor\symfony\console\Command\Command.php:255
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\console\Application.php:919
Symfony\Component\Console\Application->doRunCommand() at C:\symfony\tlt\vendor\
symfony\framework-bundle\Console\Application.php:89
Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at C:\symfon
y\tlt\vendor\symfony\console\Application.php:262
Symfony\Component\Console\Application->doRun() at C:\symfony\tlt\vendor\symfony
\framework-bundle\Console\Application.php:75
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at C:\symfony\tlt\v
endor\symfony\console\Application.php:145
Symfony\Component\Console\Application->run() at C:\symfony\tlt\bin\console:38

In PDOConnection.php line 31:

[Doctrine\DBAL\Driver\PDOException]
could not find driver


Exception trace:
() at C:\symfony\tlt\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnectio
n.php:31
Doctrine\DBAL\Driver\PDOConnection->__construct() at C:\symfony\tlt\vendor\doct
rine\dbal\lib\Doctrine\DBAL\Driver\PDOMySql\Driver.php:22
Doctrine\DBAL\Driver\PDOMySql\Driver->connect() at C:\symfony\tlt\vendor\doctri
ne\dbal\lib\Doctrine\DBAL\Connection.php:356
Doctrine\DBAL\Connection->connect() at C:\symfony\tlt\vendor\doctrine\dbal\lib\
Doctrine\DBAL\Connection.php:889
Doctrine\DBAL\Connection->executeQuery() at C:\symfony\tlt\vendor\doctrine\dbal
\lib\Doctrine\DBAL\Connection.php:843
Doctrine\DBAL\Connection->fetchAll() at C:\symfony\tlt\vendor\doctrine\dbal\lib
\Doctrine\DBAL\Schema\AbstractSchemaManager.php:206
Doctrine\DBAL\Schema\AbstractSchemaManager->listTableNames() at C:\symfony\tlt\
vendor\doctrine\dbal\lib\Doctrine\DBAL\Schema\AbstractSchemaManager.php:194
Doctrine\DBAL\Schema\AbstractSchemaManager->tablesExist() at C:\symfony\tlt\ven
dor\doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableStatus.php:47
Doctrine\Migrations\Tracking\TableStatus->isCreated() at C:\symfony\tlt\vendor\
doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableManipulator.php:55
Doctrine\Migrations\Tracking\TableManipulator->createMigrationTable() at C:\sym
fony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Configuration\Config
uration.php:317
Doctrine\Migrations\Configuration\Configuration->createMigrationTable() at C:\s
ymfony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Tools\Console\Comm
and\AbstractCommand.php:76
Doctrine\Migrations\Tools\Console\Command\AbstractCommand->initialize() at C:\s
ymfony\tlt\vendor\doctrine\doctrine-migrations-bundle\Command\MigrationsDiffDoct
rineCommand.php:41
Doctrine\Bundle\MigrationsBundle\Command\MigrationsDiffDoctrineCommand->initial
ize() at C:\symfony\tlt\vendor\symfony\console\Command\Command.php:221
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\maker-bundle\src\Maker\MakeMigration.php:79
Symfony\Bundle\MakerBundle\Maker\MakeMigration->generate() at C:\symfony\tlt\ve
ndor\symfony\maker-bundle\src\Command\MakerCommand.php:104
Symfony\Bundle\MakerBundle\Command\MakerCommand->execute() at C:\symfony\tlt\ve
ndor\symfony\console\Command\Command.php:255
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\console\Application.php:919
Symfony\Component\Console\Application->doRunCommand() at C:\symfony\tlt\vendor\
symfony\framework-bundle\Console\Application.php:89
Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at C:\symfon
y\tlt\vendor\symfony\console\Application.php:262
Symfony\Component\Console\Application->doRun() at C:\symfony\tlt\vendor\symfony
\framework-bundle\Console\Application.php:75
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at C:\symfony\tlt\v
endor\symfony\console\Application.php:145
Symfony\Component\Console\Application->run() at C:\symfony\tlt\bin\console:38

In PDOConnection.php line 27:

[PDOException]
could not find driver


Exception trace:
() at C:\symfony\tlt\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnectio
n.php:27
PDO->__construct() at C:\symfony\tlt\vendor\doctrine\dbal\lib\Doctrine\DBAL\Dri
ver\PDOConnection.php:27
Doctrine\DBAL\Driver\PDOConnection->__construct() at C:\symfony\tlt\vendor\doct
rine\dbal\lib\Doctrine\DBAL\Driver\PDOMySql\Driver.php:22
Doctrine\DBAL\Driver\PDOMySql\Driver->connect() at C:\symfony\tlt\vendor\doctri
ne\dbal\lib\Doctrine\DBAL\Connection.php:356
Doctrine\DBAL\Connection->connect() at C:\symfony\tlt\vendor\doctrine\dbal\lib\
Doctrine\DBAL\Connection.php:889
Doctrine\DBAL\Connection->executeQuery() at C:\symfony\tlt\vendor\doctrine\dbal
\lib\Doctrine\DBAL\Connection.php:843
Doctrine\DBAL\Connection->fetchAll() at C:\symfony\tlt\vendor\doctrine\dbal\lib
\Doctrine\DBAL\Schema\AbstractSchemaManager.php:206
Doctrine\DBAL\Schema\AbstractSchemaManager->listTableNames() at C:\symfony\tlt\
vendor\doctrine\dbal\lib\Doctrine\DBAL\Schema\AbstractSchemaManager.php:194
Doctrine\DBAL\Schema\AbstractSchemaManager->tablesExist() at C:\symfony\tlt\ven
dor\doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableStatus.php:47
Doctrine\Migrations\Tracking\TableStatus->isCreated() at C:\symfony\tlt\vendor\
doctrine\migrations\lib\Doctrine\Migrations\Tracking\TableManipulator.php:55
Doctrine\Migrations\Tracking\TableManipulator->createMigrationTable() at C:\sym
fony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Configuration\Config
uration.php:317
Doctrine\Migrations\Configuration\Configuration->createMigrationTable() at C:\s
ymfony\tlt\vendor\doctrine\migrations\lib\Doctrine\Migrations\Tools\Console\Comm
and\AbstractCommand.php:76
Doctrine\Migrations\Tools\Console\Command\AbstractCommand->initialize() at C:\s
ymfony\tlt\vendor\doctrine\doctrine-migrations-bundle\Command\MigrationsDiffDoct
rineCommand.php:41
Doctrine\Bundle\MigrationsBundle\Command\MigrationsDiffDoctrineCommand->initial
ize() at C:\symfony\tlt\vendor\symfony\console\Command\Command.php:221
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\maker-bundle\src\Maker\MakeMigration.php:79
Symfony\Bundle\MakerBundle\Maker\MakeMigration->generate() at C:\symfony\tlt\ve
ndor\symfony\maker-bundle\src\Command\MakerCommand.php:104
Symfony\Bundle\MakerBundle\Command\MakerCommand->execute() at C:\symfony\tlt\ve
ndor\symfony\console\Command\Command.php:255
Symfony\Component\Console\Command\Command->run() at C:\symfony\tlt\vendor\symfo
ny\console\Application.php:919
Symfony\Component\Console\Application->doRunCommand() at C:\symfony\tlt\vendor\
symfony\framework-bundle\Console\Application.php:89
Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at C:\symfon
y\tlt\vendor\symfony\console\Application.php:262
Symfony\Component\Console\Application->doRun() at C:\symfony\tlt\vendor\symfony
\framework-bundle\Console\Application.php:75
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at C:\symfony\tlt\v
endor\symfony\console\Application.php:145
Symfony\Component\Console\Application->run() at C:\symfony\tlt\bin\console:38

make:migration [--db DB] [--em [EM]] [--shard SHARD] [-h|--help] [-q|--quiet] [-
v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-
e|--env ENV] [--no-debug] [--] <command>

Reply

Hey Dennis E.!

Ah yes, I know this error! It basically means that your PHP is missing the pdo_mysql extension - that's the (unhelpful) "could not find driver" meaning - it could not find the "MySQL driver/extension for PHP". How you install it is a different story, as it differs on every operating system. I see you're on Windows - how did you install PHP? If you're using something like MAMP, it should come pre-installed with this extension. But let me know.

Cheers!

Reply

How do you open multiple terminal in command line in Mac?

Reply

Hey Faisal,

Take a look at iTerm2: https://www.iterm2.com/ - this one is very popular for Mac OS.

Cheers!

Reply
Dominik Avatar
Dominik Avatar Dominik | posted 4 years ago

"Command "make:migration" is not defined.
Did you mean one of these?
doctrine:migrations:diff
doctrine:migrations:execute
doctrine:migrations:generate
doctrine:migrations:latest
doctrine:migrations:migrate
doctrine:migrations:status
doctrine:migrations:version"
Why in my project this command is not defined? When i launch this project i just copy your code from tutorial and hit "composer install". Nothing else.

Reply

Hey Dominik

That's interesting. Try to update maker-bundle, probably it's outdated.

composer update symfony/maker-bundle

Cheers!

1 Reply
Timothy J. Avatar
Timothy J. Avatar Timothy J. | posted 4 years ago

Hi, I have run into an error with the second migration in this lesson:

An exception occurred while executing 'CREATE UNIQUE INDEX UNIQ_23A0E66989D9B62 ON article (slug)':
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes

Reading around it seems this is due to using the utf8mb4 database type and MySQL <=5.6. I fixed the problem as advised here: https://github.com/symfony/... - by reducing to length=190 in the same annotation in Article.php.

Ill be hosting on Google App Engine so should have 5.7 MySQL in production. But my local XAMPP MySQL is probably 5.5ish. Am I ok to carry on with this db, or should I change my dev db type or indeed upgrade my dev MySQL to 5.7?

Reply

Hey Timothy,

Thanks for sharing your solution with others! I think it's good when you have the same environment locally that you have on production, but it's not required, especially if you have a few projects that have different environment because it could be tricky unless you're using some virtualization tools such as Docker, etc. But, what I'd recommend is to use a continuous integration (CI) set up for your projects where having the same environment as you have on production. So, basically, it would be cool to test your project on the same environment as you have on production. Then you can do not worry about having the same environment locally.

And yeah, I'd recommend to upgrade your local web server if it's possible and not hard to do to have MySQL 5.6 at least.

Cheers!

Reply
Maxim M. Avatar
Maxim M. Avatar Maxim M. | posted 4 years ago

I spent a lot of time dealing with migrations for PostgreSQL.

In the migration, $this->addSql('CREATE SCHEMA public') always gets into the down method);
This is a problem for many people based on Google search results.

Eventually stopped on package "ruvents/doctrine-fixes-bundle", which fixes it.

But I think it's a dirty hack. If you know, maybe there's another solution?

Reply

Hey Maxim,

I suppose you're talking about this problem: https://github.com/doctrine... - and looks like it's not fixed yet, at least I see the issue is still opened, so probably no other solutions except some workarounds.

P.S. Thanks for sharing the package that helps you, probably may be useful for other users.

Cheers!

Reply
Krzysztof K. Avatar
Krzysztof K. Avatar Krzysztof K. | posted 4 years ago

How I can overwrite default command for migrations? I have different database connections and I want to get rid of default one, user should always specify against which database migration is run.

I would like to overwrite default one and make below options mandatory:
--em
--db-configuration
So it should be not possible to run migrations without above.

Is there a way that in Symfony itself that I could get rid of default connection to db? At the moment in doctrine.yaml I have specified:
default_connection: ~

and the same I did for default_entity_manager but Symfony is ignores that and uses first defined one as a default one.

Reply

Hey Krzysztof K.!

Hmm, I understand what you're saying - it makes perfect sense. Unfortunately, I don't think it's possible. The DoctrineBundle is configured at a low level to always have a default em and default connection. Here is some deep logic that proves this: https://github.com/doctrine...

So, the best option I can think of to "enforce" being explicit about which em you want is to make an "extra" em that you set as default, and which has bad connection settings. It's a lame solution, but then you would get an immediate error when using it, instead of unexpected results.

I know that's not the answer you want - but I hope it helps!

Cheers!

Reply
Krzysztof K. Avatar

Thanks Ryan, yes I have seen this class yesterday when I was investigating this, and I was wondering if it is possible to overwrite this method, I would like to extend this class and only overwrite this one method, mainly I am wondering how I can overwrite any of the symfony/bundle class. I know that this might not resolve the issue, and app will crash because there is no default value for default connection.

Is it possible to reset: doctrine.default_entity_manager after it is set?

I will test your solution, sometimes the simplest ones are the best.

Reply

Hey Krzysztof K.!

Ahhhh :). So yes, try the simplest solution first. But, indeed.... there is dark magic you can use to override *anything* you want in the service container. Without going into too many details, this is done with a "compiler pass", which you can actually implement directly in your Kernel class: https://symfony.com/blog/ne...

This "process" function is passed a ContainerBuilder, which is an object that knows everything about the container and all of its services. From here, you can change anything you want - completely remove services, replace services, change the class of a service, etc, etc. So, yes, you could (in theory) remove the doctrine.default_entity_manager entirely. However, it's very possible that there are places in the system that rely on this, and so everything will fail. If that's the case, you could also replace this service with some class that simply throws an exception when it's used. As I mentioned, this is dark magic - so... be careful ;).

Cheers!

Reply
Krzysztof K. Avatar

Thanks again Ryan.

I need to clarify something with you.

What I have at the moment is an old application which will be replaced by symfony. In order to do that I have embedded symfony inside that old one. At the moment I was able to resolve all the problems apart from the problem described above.

The truth is that I have resolved above problem too but I need to confirm my solution with you, I am not sure if my solution is an optimal one.

So in my application I have several database connections, user can select which one he wants to use.

One connection for login details is constant, but other one is dynamic.

In doctrine.yaml I have defined both connections, but dynamic one without required details to connect. Then I have created subscriber which on

KernelEvents::CONTROLLER

will modify connection details for that dynamic connection, and for that I am using Reflection to access there private properties.

I have decided to use above event because $_SESSION is available at that stage and in session I have the details what connection was selected by user.

This solution had one issue, I have to make an extra query with: "USE $database_name", otherwise I have following error:

SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected

I couldn't figure out why this is happening.

Reply

Hey Krzysztof K.!

Wow, that's a very complex & interesting setup! So, as I understand it, you basically have 2 connections: 1 login connection that *everyone* uses, and another connection that is *different* for every user - like each user has its *own* database (or, at least, can choose some dynamic database). That's pretty cool :).

I at least mostly like your solution. Btw, you could also listen on KernelEvents::REQUEST - you should also have access to the session there. It doesn't matter too much - but in the future, it's possible that something that listens on KernelEvents::REQUEST will try to make a query, and it won't be ready yet (because REQUEST comes before CONTROLLER). But, that's a small detail.

About using Reflection to access & change the private properties, this is for sure a hack. However, I'm not sure if there is a better solution - it's pretty advanced to want to change your database parameters at runtime. But, I *do* have one idea. In Symfony 4, you typically configure you database credentials using environment variables. For example, the default doctrine.yaml configuration contains this:


doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'

When you use this config, it means that, at runtime, Symfony actually reads the DATABASE_URL environment variable. And *that* means that (in theory) in your listener, you could SET this environment variable, and Doctrine would simply start using it. You could literally do: putenv('DATABASE_URL=mysql://.....');. It's worth a try - it may *still* require the "USE $database_name" hack, but we'll see :). Also, moving our listener earlier (to REQUEST) may also help with this.

Cheers!

Reply
Amy anuszewski Avatar
Amy anuszewski Avatar Amy anuszewski | posted 4 years ago

Currently, I have an app that uses a different framework. I am hoping to port it to symfony. Our app uses a different database for each customer, and has a home-built migrations system. If somebody logs in to the customer database, it checks to see if it needs to run any of the migrations. (If we know we have a big migration to run for a large customer, we make sure to have support do that at an off-peak time). So, my question is - is it possible to run doctrine migrations from within the application and not via the terminal? (If not, I'll just put together a script that cycles through all the databases and runs the migrations command)

Reply

Hey Amy anuszewski!

Ah, very interesting situation & question! And... yes... more or less :). The bad news is that the migration library doesn't make it *super* easy to configure & setup the services you need to do this. If you look in your project for a class called MigrationsMigrateDoctrineCommand, you'll see the logic needed to do it (well, most of the logic is in other classes it calls and its parent class). So yes, it's possible to create the Configuration object you need, then the Migration and migrate, but, it's not as easy as it ideally would be.

But, another option is to run the command directly from inside Symfony. This is a fine solution - here are some details https://symfony.com/doc/cur...

I hope this helps!

Cheers!

1 Reply
Amy anuszewski Avatar
Amy anuszewski Avatar Amy anuszewski | weaverryan | posted 4 years ago

Thank you. That was very helpful :).

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 4 years ago

Please, help.

I add a new property (column) to the entity, with the command

bin/console doctrine:migrations:migrate

My database table already contains some data and a new column is added with empty value for existing data in the database.

What should I do if I want to add a new column for existing fields, not with an empty value, but with a certain default value.

Reply

Hey Dmirtiy,

As I see you use migrations which is super cool! And despite the fact that Doctrine generate a migration for you to sync your DB schemas, you are totally able to write/add custom queries inside that migration. So, after "ALTER TABLE" query that adds a column you can add one more SQL query manually that will populate the new column with some data. It's a totally valid way if you have some lightweight changes, and this migration will took a few seconds. But you need more time, like about an hour to populate that new column with some data, like you need to perform API calls for to get proper data for this column - you probably better do not do it in migration. Use one-time migration commands - it's a regular Symfony console command, that you will run on your production only once to populate some columns with some data when this operation takes a long time like about an hour. So after its successful execution you can just removed it at all from your code.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "knplabs/knp-time-bundle": "^1.8", // 1.8.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "fzaninotto/faker": "^1.7", // v1.7.1
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.4.0
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}