Миграции

Часто в процессе работы над сайтом приходится вносить изменения в структуру базы данных. Заказчики добавляют много "хотелок" в процессе осознания каким они хотят видеть свой сайт. Чтобы в дальнейшем было удобно поддерживать проект, а также подключать к работе новых программистов, удобно использовать миграции.

Они будут сохранять все изменения базы в специальных php файлах и выполнив их всегда можно получить актуальное состояние базы. В Symfony для управления миграциями используется DoctrineMigrationsBundle. Подробнее с ним можно познакомить на страницах официальной документации

1. Установка

    
    composer require doctrine/doctrine-migrations-bundle "^2.0"
    
    

2. Настройка

После установки автоматически происходит подключение бандла в файле config/bundles.php

    
    // config/bundles.php

    return [
        // ...
        Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
        // ...
    ];
    
    

Настройку можно произвести в файле config/packages/doctrine_migrations.yaml, который создается автоматически.
Стандартные настройки выглядят следующим образом:

    
    # config/packages/doctrine_migrations.yaml

    doctrine_migrations:
    dir_name: '%kernel.project_dir%/src/Migrations'
    # namespace is arbitrary but should be different from App\Migrations
    # as migrations classes should NOT be autoloaded
    namespace: DoctrineMigrations
    
    

3. Базовое использование

Представим такую ситуацию, нам необходимо внедрить модуль новостей в наш проект. Для начала нужно создать сущность для новостей.

    
    php bin/console make:entity
    
    

Далее мы отвечает на соответствующие вопросы и получаем в результате примерно следующее:

    
    Add another property? Enter the property name (or press  to stop adding fields):
    > deleted

    Field type (enter ? to see all types) [string]:
    > integer

    Can this field be null in the database (nullable) (yes/no) [no]:
    > true

    updated: src/Entity/AppBundleNews.php

    Add another property? Enter the property name (or press  to stop adding fields):
        >

        Success!


        Next: When you're ready, create a migration with php bin/console make:migration

    
    

Вводим команду для создания миграции.

    
    php bin/console doctrine:database:create
    Created database `rest_api` for connection named default
    MacBook-Pro-Vaceslav-4:rest-api soprantsov$ php bin/console make:migration


      Success!


     Next: Review the new migration "src/Migrations/Version20201004180202.php"
     Then: Run the migration with php bin/console doctrine:migrations:migrate
     See https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
    
    

В результате мы получили php файл миграции, который имеет следующий вид:

        
    namespace DoctrineMigrations;

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

    /**
     * Auto-generated Migration: Please modify to your needs!
     */
    final class Version20201004180202 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->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');

            $this->addSql('CREATE TABLE app_bundle_news (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, anons LONGTEXT DEFAULT NULL, publish DATETIME DEFAULT NULL, content LONGTEXT NOT NULL, active INT DEFAULT NULL, deleted INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
        }

        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('DROP TABLE app_bundle_news');
        }
    }

        
    

4. Запуск миграции

        
    php bin/console doctrine:migrations:migrate
        
    

Функция up() выполняется при запуске миграции и создает таблицу в базе, а down() - при откате миграции, она удаляет таблицу новостей. Давайте протестируем их.

        
    php bin/console doctrine:migrations:migrate

            Application Migrations


    WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)y
    Migrating up to 20201004180202 from 0

      ++ migrating 20201004180202

         -> CREATE TABLE app_bundle_news (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, anons LONGTEXT DEFAULT NULL, publish DATETIME DEFAULT NULL, content LONGTEXT NOT NULL, active INT DEFAULT NULL, deleted INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB

      ++ migrated (took 71.6ms, used 18M memory)

      ------------------------

      ++ finished in 75.8ms
      ++ used 18M memory
      ++ 1 migrations executed
      ++ 1 sql queries
        
    

5. Откат миграции

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

    
        doctrine:migrations:execute {migration version} --down
    
    

c флагом --down

        
        php bin/console doctrine:migrations:latest
        20201004180202
        MacBook-Pro-Vaceslav-4:rest-api soprantsov$ php bin/console doctrine:migrations:execute 20201004180202 --down
        WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y

          -- reverting 20201004180202

             -> DROP TABLE app_bundle_news

          -- reverted (took 25ms, used 18M memory)

        
    

6. Добавление поля

В дальнейшем нам может понадобится добавить дополнительные поля к нашим сущностям.
Добавим поле "Дата удаления" к нашим новостям.

        
    /**
     * Дата удаления
     * @var $deleted_date \DateTime
     * @ORM\Column (type="datetime", nullable=true)
     */
    private $deleted_date;
        
    

Сгенерируем геттеры и сеттеры

        
        php bin/console make:entity App --regenerate
        
    

Обычно для каждого проекта я использую 2 базы данных, для разработики и тестов, а также для prod режима.

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

Далее необходимо создать миграцию изменений базы. Для режима dev используется команда:

        
        php bin/console doctrine:migrations:diff -e prod
        
    

Теперь накатим миграцию на продакшн

        
        php bin/console doctrine:migrations:migrate -e prod
        
    

На этом вроде как все, успехов в работе.