You are browsing a version that is no longer maintained.

DoctrineMigrationsBundle

Database migrations are a way to safely update your database schema both locally and on production. Instead of running the doctrine:schema:update command or applying the database changes manually with SQL statements, migrations allow to replicate the changes in your database schema in a safe manner.

Migrations are available in Symfony applications via the DoctrineMigrationsBundle, which uses the external Doctrine Database Migrations library. Read the documentation of that library if you need a general introduction about migrations.

Installation

Run this command in your terminal:

$ composer require doctrine/doctrine-migrations-bundle "^3.0"

If you don't use Symfony Flex, you must enable the bundle manually in the application:

1// config/bundles.php // in older Symfony apps, enable the bundle in app/AppKernel.php return [ // ... Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], ];
2
3
4
5
6

Configuration

If you use Symfony Flex, the doctrine_migrations.yaml config file is created automatically. Otherwise, create the following file and configure it for your application:

1# config/packages/doctrine_migrations.yaml doctrine_migrations: # List of namespace/path pairs to search for migrations, at least one required migrations_paths: 'App\Migrations': '%kernel.project_dir%/src/App' 'AnotherApp\Migrations': '/path/to/other/migrations' 'SomeBundle\Migrations': '@SomeBundle/Migrations' # List of additional migration classes to be loaded, optional migrations: - 'App\Migrations\Version123' - 'App\Migrations\Version123' # Connection to use for the migrations connection: default # Entity manager to use for migrations. This overrides the "connection" setting. em: default storage: # Default (SQL table) metadata storage configuration table_storage: table_name: 'doctrine_migration_versions' version_column_name: 'version' version_column_length: 192 executed_at_column_name: 'executed_at' # Possible values: "BY_YEAR", "BY_YEAR_AND_MONTH", false organize_migrations: false # Path to your custom migrations template custom_template: ~ # Run all migrations in a transaction. all_or_nothing: false # Adds an extra check in the generated migrations to ensure that is executed on the same database type. check_database_platform: true # Whether or not to wrap migrations in a single transaction. transactional: true # Whether or not to enable the profiler collector to calculate and visualize migration status. This adds some queries overhead. # enable_profiler: false services: # Custom migration sorting service id 'Doctrine\Migrations\Version\Comparator': ~ # Custom migration classes factory 'Doctrine\Migrations\Version\MigrationFactory': ~ factories: # Custom migration sorting service id via callables (MyCallableFactory must be a callable) 'Doctrine\Migrations\Version\Comparator': 'MyCallableFactory'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  • The services node allows you to provide custom services to the underlying DependencyFactory part of doctrine/migrations.
  • The node factories is similar to services, with the difference that it accepts only callables.

The provided callable must return the service to be passed to the DependencyFactory. The callable will receive as first argument the DependencyFactory itself, allowing you to fetch other dependencies from the factory while instantiating your custom dependencies.

Usage

All of the migrations functionality is contained in a few console commands:

doctrine
 doctrine:migrations:current                [current] Outputs the current version.
 doctrine:migrations:diff                   [diff] Generate a migration by comparing your current database to your mapping information.
 doctrine:migrations:dump-schema            [dump-schema] Dump the schema for your database to a migration.
 doctrine:migrations:execute                [execute] Execute a single migration version up or down manually.
 doctrine:migrations:generate               [generate] Generate a blank migration class.
 doctrine:migrations:latest                 [latest] Outputs the latest version number
 doctrine:migrations:migrate                [migrate] Execute a migration to a specified version or the latest available version.
 doctrine:migrations:rollup                 [rollup] Roll migrations up by deleting all tracked versions and inserting the one version that exists.
 doctrine:migrations:status                 [status] View the status of a set of migrations.
 doctrine:migrations:up-to-date             [up-to-date] Tells you if your schema is up-to-date.
 doctrine:migrations:version                [version] Manually add and delete migration versions from the version table.
 doctrine:migrations:sync-metadata-storage  [sync-metadata-storage] Ensures that the metadata storage is at the latest version.
 doctrine:migrations:list                   [list-migrations] Display a list of all available migrations and their status.

Start by getting the status of migrations in your application by running the status command:

$ php bin/console doctrine:migrations:status

This command will show you generic information about the migration status, such as how many migrations have been already executed, which still need to run, and the database in use.

Now, you can start working with migrations by generating a new blank migration class. Later, you'll learn how Doctrine can generate migrations automatically for you.

$ php bin/console doctrine:migrations:generate

Have a look at the newly generated migration class and you will see something like the following:

1declare(strict_types=1); namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ final class Version20180605025653 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 } public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs } }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

If you run the status command again it will now show that you have one new migration to execute:

$ php bin/console doctrine:migrations:status --show-versions

Now you can add some migration code to the up() and down() methods and finally migrate when you're ready:

$ php bin/console doctrine:migrations:migrate 'DoctrineMigrations\Version20180605025653'

For more information on how to write the migrations themselves (i.e. how to fill in the up() and down() methods), see the official Doctrine Migrations documentation.

Running Migrations during Deployment

Of course, the end goal of writing migrations is to be able to use them to reliably update your database structure when you deploy your application. By running the migrations locally (or on a beta server), you can ensure that the migrations work as you expect.

When you do finally deploy your application, you just need to remember to run the doctrine:migrations:migrate command. Internally, Doctrine creates a migration_versions table inside your database and tracks which migrations have been executed there. So, no matter how many migrations you've created and executed locally, when you run the command during deployment, Doctrine will know exactly which migrations it hasn't run yet by looking at the migration_versions table of your production database. Regardless of what server you're on, you can always safely run this command to execute only the migrations that haven't been run yet on that particular database.

Skipping Migrations

You can skip single migrations by explicitly adding them to the migration_versions table:

$ php bin/console doctrine:migrations:version 'App\Migrations\Version123' --add

Pay attention to the single quotes (') used in the command above, without them or with the double quotes (") the command will not work properly.

Doctrine will then assume that this migration has already been run and will ignore it.

Migration Dependencies

Migrations can have dependencies on external services (such as geolocation, mailer, data processing services...) that can be used to have more powerful migrations. Those dependencies are not automatically injected into your migrations but need to be injected using custom migrations factories.

Here is an example on how to inject the service container into your migrations:

1# config/packages/doctrine_migrations.yaml doctrine_migrations: services: 'Doctrine\Migrations\Version\MigrationFactory': 'App\Migrations\Factory\MigrationFactoryDecorator' # config/services.yaml services: Doctrine\Migrations\Version\DbalMigrationFactory: ~ App\Migrations\Factory\MigrationFactoryDecorator: decorates: Doctrine\Migrations\Version\DbalMigrationFactory arguments: ['@App\Migrations\Factory\MigrationFactoryDecorator.inner', '@service_container']
2
3
4
5
6
7
8
9
10
11
1declare(strict_types=1); namespace App\Migrations\Factory; use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\Version\MigrationFactory; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; class MigrationFactoryDecorator implements MigrationFactory { private $migrationFactory; private $container; public function __construct(MigrationFactory $migrationFactory, ContainerInterface $container) { $this->migrationFactory = $migrationFactory; $this->container = $container; } public function createVersion(string $migrationClassName): AbstractMigration { $instance = $this->migrationFactory->createVersion($migrationClassName); if ($instance instanceof ContainerAwareInterface) { $instance->setContainer($this->container); } return $instance; } }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

If your migration class implements the interface Symfony\Component\DependencyInjection\ContainerAwareInterface this bundle will automatically inject the default symfony container into your migration class (this because the MigrationFactoryDecorator shown in this example is the default migration factory used by this bundle).

Generating Migrations Automatically

In reality, you should rarely need to write migrations manually, as the migrations library can generate migration classes automatically by comparing your Doctrine mapping information (i.e. what your database should look like) with your actual current database structure.

For example, suppose you create a new User entity and add mapping information for Doctrine's ORM:

1// src/Entity/User.php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="hello_user") */ class User { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(type="string", length=255) */ private $name;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

With this information, Doctrine is now ready to help you persist your new User object to and from the user table. Of course, this table doesn't exist yet! Generate a new migration for this table automatically by running the following command:

$ php bin/console doctrine:migrations:diff

You should see a message that a new migration class was generated based on the schema differences. If you open this file, you'll find that it has the SQL code needed to create the user table. Next, run the migration to add the table to your database:

$ php bin/console doctrine:migrations:migrate

The moral of the story is this: after each change you make to your Doctrine mapping information, run the doctrine:migrations:diff command to automatically generate your migration classes.

If you do this from the very beginning of your project (i.e. so that even the first tables were loaded via a migration class), you'll always be able to create a fresh database and run your migrations in order to get your database schema fully up to date. In fact, this is an easy and dependable workflow for your project.

If you don't want to use this workflow and instead create your schema via doctrine:schema:create, you can tell Doctrine to skip all existing migrations:

$ php bin/console doctrine:migrations:version --add --all

Otherwise Doctrine will try to run all migrations, which probably will not work.

Manual Tables

It is a common use case, that in addition to your generated database structure based on your doctrine entities you might need custom tables. By default such tables will be removed by the doctrine:migrations:diff command.

If you follow a specific scheme you can configure doctrine/dbal to ignore those tables. Let's say all custom tables will be prefixed by t_. In this case you just have to add the following configuration option to your doctrine configuration:

1doctrine: dbal: schema_filter: ~^(?!t_)~
2
3

This ignores the tables, and any named objects such as sequences, on the DBAL level and they will be ignored by the diff command.

Note that if you have multiple connections configured then the schema_filter configuration will need to be placed per-connection.

Troubleshooting out of sync metadata storage issue

doctrine/migrations relies on a properly configured Database server version in the connection string to manage the table storing the migrations, also known as the metadata storage.

If you encounter the error The metadata storage is not up to date, please run the sync-metadata-storage command to fix this issue. when running the command doctrine:migrations:migrate or the suggested command itself doctrine:migrations:sync-metadata-storage please check the database connection string, and make sure that the proper server version is defined. If you are running a MariaDB database, you should prefix the server version with mariadb- (ex: mariadb-10.2.12). See the configuring_database section.

Example connection string for MariaDB:

$ DATABASE_URL=mysql://root:@127.0.0.1:3306/testtest?serverVersion=mariadb-10.4.11