corytech/doctrine-migrations-multiple-database-bundle
composer require avaibooksports/doctrine-migrations-multiple-database-bundle
config/bundles.php:
AvaiBookSports\Bundle\MigrationsMutlipleDatabase\DoctrineMigrationsMultipleDatabaseBundle::class => ['all' => true],
config/packages/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'
php bin/console make:migration --em=geonames
Run migrations for a specific EM:
php bin/console doctrine:migrations:migrate --em=geonames
Multi-Database Projects:
default EM for primary DB (e.g., users, posts).geonames EM for read-only or specialized DBs (e.g., geonames, analytics).migrations/
├── Main/ # Default EM migrations
│ └── VersionYYYYMMDDHMS.php
└── Geonames/ # Geonames EM migrations
└── VersionYYYYMMDDHMS.php
Command-Line Patterns:
# Migrate only the 'geonames' EM
php bin/console doctrine:migrations:migrate --em=geonames
# Execute a specific migration for 'default' EM
php bin/console doctrine:migrations:execute --em=default --query="ALTER TABLE users ADD COLUMN active BOOLEAN"
php bin/console doctrine:migrations:diff --em=geonames --dry-run
CI/CD Pipelines:
# Run in parallel (e.g., GitHub Actions)
php bin/console doctrine:migrations:migrate --em=default & php bin/console doctrine:migrations:migrate --em=geonames
--allow-no-migration to skip EMs with no pending migrations:
php bin/console doctrine:migrations:migrate --em=default,geonames --allow-no-migration
Entity Manager Awareness:
config/packages/doctrine.yaml defines all EMs:
doctrine:
dbal:
connections:
default: { url: '%env(DATABASE_URL)' }
geonames: { url: '%env(GEONAMES_DATABASE_URL)' }
orm:
entity_managers:
default:
connection: default
mappings: [App]
geonames:
connection: geonames
mappings: [App\Geonames]
Migration Dependencies:
--em with doctrine:migrations:execute for cross-EM dependencies:
# Create a lookup table in 'default' EM first
php bin/console doctrine:migrations:execute --em=default --query="CREATE TABLE em_lookups (...)"
# Reference it in 'geonames' EM migrations
php bin/console doctrine:migrations:execute --em=geonames --query="ALTER TABLE geonames ADD CONSTRAINT (...) FOREIGN KEY (...) REFERENCES em_lookups(id)"
Command Fallback:
--em is omitted, the bundle falls back to doctrine_migrations.yaml. Ensure this file exists and is configured for the default EM to avoid silent failures.--em or ensure doctrine_migrations.yaml is properly set up.Migration Path Conflicts:
DoctrineMigrations\Main or DoctrineMigrations\Geonames already exist in vendor/, the bundle will overwrite them. This can cause issues if you’re using custom migration namespaces.App\Migrations\Main) and update the config:
doctrine_migrations_multiple_database:
entity_managers:
default:
migrations_paths:
App\Migrations\Main: '%kernel.project_dir%/migrations/Main'
Connection Order:
doctrine_migrations_multiple_database.yaml. If migrations depend on connection state (e.g., sequences, triggers), define dependencies explicitly in the config or use doctrine:migrations:execute.Schema Validation:
doctrine:schema:validate without --em will skip the bundle’s configuration. Always specify --em for multi-EM setups:
php bin/console doctrine:schema:validate --em=default,geonames
Migration Generation:
orm.entity_managers config in doctrine.yaml to avoid "entity not found" errors.Check Active EM:
php bin/console doctrine:migrations:migrate --em=geonames --verbose
[DoctrineMigrationsMultipleDatabaseBundle] Using EntityManager 'geonames'
Inspect Migration Paths:
EntityManagerAwareMigrationCommand (e.g., in src/Command/MigrationCommand.php) to confirm paths are resolved correctly.Connection Issues:
php bin/console doctrine:query:sql "SELECT 1" --em=geonames
Lock File Conflicts:
migrations/Geonames/Version*.php.lock). If migrations hang, manually delete the .lock files for the problematic EM.Custom Migration Namespaces:
MigrationPathsResolver service. Example:
# config/services.yaml
AvaiBookSports\Bundle\MigrationsMutlipleDatabase\DependencyInjection\Compiler\MigrationPathsResolverPass:
arguments:
- ['App\Migrations\Main', '%kernel.project_dir%/migrations/Main']
- ['App\Migrations\Geonames', '%kernel.project_dir%/migrations/Geonames']
Dynamic EM Configuration:
EntityManagerConfigLoader:
// src/DependencyInjection/EntityManagerConfigLoader.php
class DynamicEntityManagerConfigLoader implements EntityManagerConfigLoaderInterface
{
public function load(): array
{
return [
'default' => ['migrations_paths' => ['App\Migrations\Main' => $this->getPath('Main')]],
'geonames' => ['migrations_paths' => ['App\Migrations\Geonames' => $this->getPath('Geonames')]],
];
}
}
Post-Migration Hooks:
PostMigrationEventSubscriber:
// src/EventSubscriber/PostGeonamesMigrationSubscriber.php
class PostGeonamesMigrationSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [PostMigrationEvent::NAME => ['onPostMigration', 0]];
}
public function onPostMigration(PostMigrationEvent $event): void
{
if ($event->getEntityManagerName() === 'geonames') {
$this->seedGeonamesData();
}
}
}
Parallel Migration Execution:
Process component. Example:
// src/Command/ParallelMigrationsCommand.php
class ParallelMigrationsCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$processes = [];
foreach ($this->getEntityManagers() as $em) {
$process = new Process(['php', 'bin/console', 'doctrine:migrations:migrate', '--em='.$em]);
How can I help you explore Laravel packages today?