Как в Symfony 3.4 работать с миграциями БД

Симфони мать рот его ебал! Худший php-фреймворк не перестаёт удивлять своей ебанутостью и полным отсутствием user-friendly. На этот раз Symfony 3.4 удивил тем, что в нём нет механизма миграций! То есть жирная ОРМ в нём есть, куча никому не нужны модулей есть, а миграций нет. Их нужно ставить отдельным модулем:

composer require doctrine/doctrine-migrations-bundle

Далее нужно добавить в файл app\AppKernel.php такую строку:

new \Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),

Затем добавим в файл app/config/config.yml:

doctrine_migrations:
    dir_name: "%kernel.root_dir%/DoctrineMigrations"
    namespace: Application\Migrations
    table_name: migration_versions
    name: Application Migrations
    organize_migrations: false # Version >=1.2 Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false

Ну теперь то можно приступать к созданию миграций! Выполняем:

php bin/console doctrine:migrations:generate

На выводе будет что-то типа:

Generated new migration class to "app/DoctrineMigrations/Version20180724141244.php"

Окей, открываем этот файл и херачим миграцию. Но нет, блять! Нельзя просто так взять и захуячить миграцию в этом припизднутом Симфони! Точнее можно легко и просто изменить структуру БД в миграции или выполнить SQL-запрос! Но для работы с entity-моделями нужно изъебнуться и изменить каждую миграцию. В документации этот трюк называют Container Aware Migrations. Во-первых, добавить в неё реализацию интерфейса ContainerAwareInterface:

final class Version20180724141244 extends AbstractMigration implements ContainerAwareInterface {

И собственно саму реализацию:

/**
     * @var Container
     */
    private $container;
    public function setContainer(ContainerInterface $container = null) {
        $this->container = $container;
    }

Либо заюзать трейт:

use ContainerAwareTrait;

И юзать это примерно так:

public function up(Schema $schema) : void {
        $Currency = new Currency();
        $Currency->setCode('usd');
        $Currency->setName('USD');
        $em = $this->container->get('doctrine.orm.entity_manager');
        $em->persist($Currency);
        $em->flush();
    }

А для отката так:

public function down(Schema $schema) : void {
        $em = $this->container->get('doctrine.orm.entity_manager');
        $Currency = $em->getRepository(Currency::class)->findByCode('usd');
        $em->remove($Currency);
        $em->flush();
    }

Ничего более уебанского и неудобного блять нельзя было придумать! В очко ебал разработчиком Symfony! Теперь можно запустить выполнение миграций:

php bin/console doctrine:migrations:migrate

В ответ будет что-то  типа:

Migrating up to 20180724141244 from 0
  ++ migrating 20180724141244
Migration 20180724141244 was executed but did not result in any SQL statements.
  ++ migrated (0.03s)
  ------------------------
  ++ finished in 0.03s
  ++ 1 migrations executed
  ++ 0 sql queries

Посмотреть статус миграций можно так:

php bin/console doctrine:migrations:status --show-versions
== Configuration
    >> Name:                                               Application Migrations
    >> Database Driver:                                    pdo_mysql
    >> Database Name:                                      api
    >> Configuration Source:                               manually configured
    >> Version Table Name:                                 migration_versions
    >> Version Column Name:                                version
    >> Migrations Namespace:                               Application\Migrations
    >> Migrations Directory:                               /var/www/app/current/app/DoctrineMigrations
    >> Previous Version:                                   0
    >> Current Version:                                    2018-07-24 14:12:44 (20180724141244)
    >> Next Version:                                       Already at latest version
    >> Latest Version:                                     2018-07-24 14:12:44 (20180724141244)
    >> Executed Migrations:                                1
    >> Executed Unavailable Migrations:                    0
    >> Available Migrations:                               1
    >> New Migrations:                                     0
 == Available Migration Versions
    >> 2018-07-24 14:12:44 (20180724141244)                migrated

Для отката миграций можно воспользоваться такой командой:

php bin/console doctrine:migrations:execute --down 20180724141244

Важно, это команда откатывает только одну указанную миграцию, а не последовательно все миграции до указанной.

Можно методично накатывать и откатывать миграции с помощью таких команд:

php bin/console doctrine:migrations:migrate prev
php bin/console doctrine:migrations:migrate next

Также существуют и другие указатели: first, latest, prev, current и next. Можно использовать выражения, например current+3. При использовании first будут откачены все миграции!

Что можно почитать на эту тему?

  • https://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
  • https://www.sgalinski.de/typo3-agentur/technik/how-to-work-with-doctrine-migrations-in-symfony/
    http://www.inanzzz.com/index.php/post/9z1m/handling-database-migrations-with-doctrine-migrations-bundle-in-symfony