On doctrine/doctrine-migrations-bundle
2.* this was relatively simple - use the --em
option (and use ContainerAwareInterface
to skip any migrations from a different em/connection).
Now (on doctrine/doctrine-migrations-bundle
3.2.2), it seems the --em
option is ignored, and the default em/connection is always specified, meaning the migrations for the default em are applied to every database. Edit: As pointed out in comments - --em
is not ignored, it's passed through directly, it's rather our ContainerAwareInterface
approach that's no longer valid.
There is a lot of conflicting information on how to set this up, some suggesting it should "just work" (Symfony Docs) and other describing workarounds (Issue):
https://symfony.com/doc/current/doctrine/multiple_entity_managers.html https://github.com/doctrine/DoctrineMigrationsBundle/issues/38
How does one configure this new version (3) of doctrine/doctrine-migrations-bundle
to apply migrations only to their matching entity/db?
Edit: I've included below our config previous to upgrading, which along with the ContainerAwareInterface
connection filtering approach, allowed filtering migrations to run only against the appropriate entity manager.
Our existing "doctrine/doctrine-bundle": "1.12.8"
config (shortened, but shows multiple entity managers):
doctrine:
dbal:
connections:
default:
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
driver: '%database_driver%'
server_version: mariadb-10.4.11
host: '%database_host%'
port: '%database_port%'
dbname: autotempest
user: '%database_user%'
password: '%database_password%'
mapping_types:
enum: string
model:
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
driver: '%database_driver%'
server_version: mariadb-10.4.11
host: '%database_host%'
port: '%database_port%'
dbname: autotempest_models
user: '%database_user%'
password: '%database_password%'
mapping_types:
enum: string
wrapper_class: App\Doctrine\ConnectionWrapper\ConnectionModel
persistent: true
orm:
auto_generate_proxy_classes: '%kernel.debug%'
entity_managers:
default:
connection: default
mappings:
App:
type: 'annotation'
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
model:
connection: model
mappings:
TempestModelBundle:
type: 'annotation'
dir: 'Entity'
prefix: 'Tempest\Bundle\ModelBundle\Entity'
Our "doctrine/doctrine-migrations-bundle": "2.1.2"
config:
doctrine_migrations:
dir_name: '%kernel.project_dir%/src/Migrations'
namespace: Application\Migrations
Also mentioned in my question, there is an open issue on the DoctrineMigrationsBundle
from 2012 describing the problem of dealing with migrations when using multiple entity managers: https://github.com/doctrine/DoctrineMigrationsBundle/issues/38. It seems there are several options for workarounds to this issue as described there, we just needed to dig and try each of them to find the best one for our situation.
On Symfony 3, we were using the ContainerAwareInterface
approach. Described in the above issue:
Currently this can be achieved by using Container Aware Migrations. If one can have the service container injected, he can obtain an instance of some entity manager and its connection.
This is no longer really a valid solution when moving to Symfony 4 however, due to ContainerAware
classes being deprecated in favor of dependency injection.
Another approach mentioned in the github issue above. The idea here is to have a separate configuration file for each entity manager like the following:
# config/packages/migrations/base.yaml
em: default
transactional: false
migrations_paths:
Hyra\Migrations\Base: src/Migrations/Base
table_storage:
table_name: migration_versions
This is passed directly to the command, along with the entity manager like this: bin/console doctrine:migrations:migrate --em default --configuration config/packages/migrations/base.yaml
. These separate config files replace the single config/packages/doctrine_migrations.yaml
configuration file.
This was also not viable for us, as we still needed to inject services into our migrations using the services
configuration option of DoctrineMigrationsBundle
, and --configuration
only passes configuration options directly through to doctrine/migrations
, which doesn't support the services
configuration option.
Initially on DoctrineMigrationsBundle
3.0, this approach was complicated by the fact that the --em
and --conn
options were dropped completely, so it was also necessary to create a wrapper on top of the DoctrineMigrationsBundle
commands to re-implement these options (described in more detail here). This is no longer necessary on DoctrineMigrationsBundle
3.1+ (which restored these options).
DoctrineMigrationsMultipleDatabaseBundle
Also mentioned in the github issue thread, this bundle implements what we needed exactly (and ended up using) - per-entity configuration for DoctrineMigrationsBundle
, so we can also include our services
config for migration dependency injection. Initially I misconfigured this - it's important that the base doctrine_migrations.yaml
config only includes config for the default entity manager. Sample working config provided by the package author (version 0.3.3):
# doctrine.yaml
doctrine:
dbal:
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
server_version: mariadb-10.1.26
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
mapping_types:
enum: string
geonames:
url: '%env(resolve:GEONAMES_DATABASE_URL)%'
server_version: mariadb-10.1.26
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
mapping_types:
enum: string
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '5.7'
orm:
auto_generate_proxy_classes: true
default_entity_manager: default
entity_managers:
default:
connection: default
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/App/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
geonames:
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
connection: geonames
mappings:
Geonames:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/App/Entity/Geonames'
prefix: 'App\Entity\Geonames'
alias: Geonames
# doctrine_migrations.yaml
doctrine_migrations:
em: default
migrations_paths:
DoctrineMigrations: '%kernel.project_dir%/migrations/Main'
# doctrine_migrations_multiple_database.yaml
doctrine_migrations_multiple_database:
entity_managers:
default:
migrations_paths:
DoctrineMigrations\Main: '%kernel.project_dir%/migrations/Main'
geonames:
migrations_paths:
DoctrineMigrations\Geonames: '%kernel.project_dir%/migrations/Geonames'