├── .editorconfig ├── .github └── stale.yml ├── .gitignore ├── .php_cs.dist ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── phpstan.neon └── src ├── Command └── Doctrine │ ├── AbstractCommand.php │ ├── CurrentCommand.php │ ├── DiffCommand.php │ ├── DumpSchemaCommand.php │ ├── ExecuteCommand.php │ ├── GenerateCommand.php │ ├── LatestCommand.php │ ├── ListCommand.php │ ├── MigrateCommand.php │ ├── RollupCommand.php │ ├── StatusCommand.php │ ├── SyncMetadataCommand.php │ ├── UpToDateCommand.php │ └── VersionCommand.php ├── Configuration └── Configuration.php ├── DependencyInjection ├── Configuration.php └── DoctrineMigrationsMultipleDatabaseExtension.php ├── DoctrineMigrationsMultipleDatabaseBundle.php └── Resources └── config └── services.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.php] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [composer.json] 10 | indent_style = tab 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | .php_cs.cache -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | in('src') 4 | ; 5 | return PhpCsFixer\Config::create() 6 | ->setRules([ 7 | '@Symfony' => true, 8 | '@PSR2' => true, 9 | 'class_attributes_separation' => ['elements' => ['const', 'method', 'property']], 10 | // 'strict_param' => true, 11 | 'array_syntax' => ['syntax' => 'short'], 12 | ]) 13 | ->setFinder($finder) 14 | ; 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.3] 8 | ### Added 9 | - Supported command `doctrine:migrations:execute` 10 | - Supported command `doctrine:migrations:generate` 11 | - Supported command `doctrine:migrations:rollup` 12 | - Supported command `doctrine:migrations:dump-schema` 13 | - Supported options `--namespace` and `--filter-expression` for `doctrine:migrations:diff` command 14 | 15 | ## [0.2] 16 | ### Added 17 | - Supported command `doctrine:migrations:current` 18 | - Supported command `doctrine:migrations:latest` 19 | - Supported command `doctrine:migrations:list` 20 | - Supported command `doctrine:migrations:status` 21 | - Supported command `doctrine:migrations:sync-metadata-storage` 22 | - Supported command `doctrine:migrations:up-to-date` 23 | 24 | ### Changed 25 | - Internal class renaming 26 | - Command description as the original one in `doctrine:migrations:diff` and `doctrine:migrations:version` 27 | 28 | ## [0.1.1] 29 | ### Added 30 | - Project cleanup 31 | 32 | ## [0.1] 33 | ### Added 34 | - Initial support for configuration files 35 | - Initial override of [DoctrineMigrationsBundle](https://github.com/doctrine/DoctrineMigrationsBundle) commands 36 | - Ability to execute migrations commands to all entity managers, or filtered by the option `--em=default` 37 | - Supported command `doctrine:migrations:diff` 38 | - Supported command `doctrine:migrations:migrate` 39 | - Supported command `doctrine:migrations:version` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AvaiBookSports.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DoctrineMigrationsMultipleDatabaseBundle 2 | 3 | This bundle extends the [DoctrineMigrationsBundle](https://github.com/doctrine/DoctrineMigrationsBundle) functionality 4 | in a hacky and dirty way to provide a easy way to configure migrations paths for multiple entity managers. 5 | 6 | ## Configuration 7 | 8 | - Install the package by running `composer require avaibooksports/doctrine-migrations-multiple-database-bundle` 9 | - Go to your `config/bundles.php` file and register the bundle: 10 | ```php 11 | AvaiBookSports\Bundle\MigrationsMutlipleDatabase\DoctrineMigrationsMultipleDatabaseBundle::class => ['all' => true], 12 | ``` 13 | - Finally, create a file in `config/packages/` called `doctrine_migrations_multiple_database.yaml` and follow the next example: 14 | ```yaml 15 | doctrine_migrations_multiple_database: 16 | entity_managers: 17 | default: 18 | migrations_paths: 19 | DoctrineMigrations\Main: '%kernel.project_dir%/migrations/Main' 20 | geonames: 21 | migrations_paths: 22 | DoctrineMigrations\Geonames: '%kernel.project_dir%/migrations/Geonames' 23 | ``` 24 | - You can leave your `doctirne_migrations.yaml` file untouched. Unmapped commands will fallback to that configuration, and if you need to disable this bundle everything should work as always. 25 | 26 | ## Usage 27 | 28 | Just call the same commands as always, with the same parameters. See the [supported commands](#supported-commands) 29 | 30 | You can run a command for a specific entity manager adding the option `--em=example` 31 | 32 | If you call any of the supported commands, they will work as always iterating over all the defined configurations. 33 | 34 | For those commands who has a `--namespace` option, you have to provide always the correct entity manager. 35 | Otherwise, it will iterate over every entity manager, and will eventually fail. If you want to suggest a better workflow, please [open an issue](../../issues)! 36 | 37 | ### Supported configuration 38 | 39 | For now, all configuration parameters should work except `connection` and `em`, because we are already specifying which entity manager we want to connect. 40 | 41 | This would be the [example configuration of DoctrineMigrationsBundle](https://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html#configuration) translated to this bundle: 42 | 43 | ```yaml 44 | # config/packages/doctrine_migrations_multiple_database.yaml 45 | 46 | 47 | doctrine_migrations_multiple_database: 48 | entity_managers: 49 | default: 50 | # List of namespace/path pairs to search for migrations, at least one required 51 | migrations_paths: 52 | 'App\Migrations': 'src/App' 53 | 'AnotherApp\Migrations': '/path/to/other/migrations' 54 | 'SomeBundle\Migrations': '@SomeBundle/Migrations' 55 | 56 | # List of additional migration classes to be loaded, optional 57 | migrations: 58 | - 'App\Migrations\Version123' 59 | - 'App\Migrations\Version123' 60 | 61 | storage: 62 | # Default (SQL table) metadata storage configuration 63 | table_storage: 64 | table_name: 'doctrine_migration_versions' 65 | version_column_name: 'version' 66 | version_column_length: 1024 67 | executed_at_column_name: 'executed_at' 68 | execution_time_column_name: 'execution_time' 69 | 70 | # Possible values: "BY_YEAR", "BY_YEAR_AND_MONTH", false 71 | organize_migrations: false 72 | 73 | # Path to your custom migrations template 74 | custom_template: ~ 75 | 76 | # Run all migrations in a transaction. 77 | all_or_nothing: false 78 | 79 | # Adds an extra check in the generated migrations to ensure that is executed on the same database type. 80 | check_database_platform: true 81 | 82 | services: 83 | # Custom migration sorting service id 84 | 'Doctrine\Migrations\Version\Comparator': ~ 85 | 86 | # Custom migration classes factory 87 | 'Doctrine\Migrations\Version\MigrationFactory': ~ 88 | 89 | factories: 90 | # Custom migration sorting service id via callables (MyCallableFactory must be a callable) 91 | 'Doctrine\Migrations\Version\Comparator': 'MyCallableFactory' 92 | ``` 93 | 94 | ### Supported commands 95 | 96 | - `doctrine:migrations:current` 97 | - `doctrine:migrations:diff` 98 | - `doctrine:migrations:dump-schema` 99 | - `doctrine:migrations:execute` 100 | - `doctrine:migrations:generate` 101 | - `doctrine:migrations:latest` 102 | - `doctrine:migrations:list` 103 | - `doctrine:migrations:migrate` 104 | - `doctrine:migrations:rollup` 105 | - `doctrine:migrations:status` 106 | - `doctrine:migrations:sync-metadata-storage` 107 | - `doctrine:migrations:up-to-date` 108 | - `doctrine:migrations:version` 109 | 110 | ## Pitfalls 111 | 112 | This package is being actively developed to satisfy a very specific scenario in our workflow, but we wanted to share 113 | this solution with more people struggling with this particular need. 114 | 115 | As we are basing our configuration in YAML files, XML and PHP formats are not tested right now. We would love to have 116 | [feedback](../../issues) from you if you have any problems configuring the bundle. Unit tests should come sooner or later. 117 | 118 | Also, we are supporting partially the configuration parameters, and not all commands are mapped. 119 | 120 | All releases tagged like `0.x` will be affected by this pitfalls, and release `1.0` will cover a full configuration file 121 | and all commands. 122 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "avaibooksports/doctrine-migrations-multiple-database-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Symfony DoctrineMigrationsMultipleDatabaseBundle", 5 | "keywords": ["DBAL", "Migrations", "Schema", "Multiple databases"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Pablo Largo", 10 | "email": "devnix.code@gmail.com" 11 | }, 12 | { 13 | "name": "AvaiBook Sports", 14 | "email": "desarrollo@avaibooksports.com", 15 | "homepage": "https://www.avaibooksports.com" 16 | } 17 | ], 18 | "require": { 19 | "doctrine/doctrine-bundle": "~1.0|~2.0", 20 | "doctrine/migrations": "~3.0", 21 | "php": ">=7.2", 22 | "symfony/framework-bundle": "~3.4|~4.0|~5.0|~6.0", 23 | "doctrine/doctrine-migrations-bundle": "^3" 24 | }, 25 | "require-dev": { 26 | "phpstan/phpstan": "^0.12.48", 27 | "phpstan/phpstan-symfony": "^0.12.8", 28 | "phpstan/phpstan-strict-rules": "^0.12.5", 29 | "doctrine/orm": "^2.6", 30 | "friendsofphp/php-cs-fixer": "^2.16" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "AvaiBookSports\\Bundle\\MigrationsMutlipleDatabase\\": "src/" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | paths: 4 | - src 5 | ignoreErrors: 6 | - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#' 7 | - '#should be contravariant with parameter \$configs \(array\) of method Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface::load\(\)#' 8 | 9 | includes: 10 | - vendor/phpstan/phpstan-strict-rules/rules.neon 11 | -------------------------------------------------------------------------------- /src/Command/Doctrine/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 29 | } 30 | 31 | /** 32 | * @throws RuntimeException 33 | * 34 | * @return DependencyFactory[] 35 | */ 36 | public function getDependencyFactories(string $entityManager = null): array 37 | { 38 | $dependencyFactories = []; 39 | 40 | if (null === $entityManager || '' === $entityManager) { 41 | $dependencyFactories = $this->configuration->getDependencyFactories(); 42 | } elseif (null !== $this->configuration->getConfigurationByEntityManagerName($entityManager)) { 43 | $dependencyFactories = [$this->configuration->getConfigurationByEntityManagerName($entityManager)]; 44 | } 45 | 46 | if (0 === count($dependencyFactories)) { 47 | throw new RuntimeException('No entity manager found'); 48 | } 49 | 50 | return $dependencyFactories; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Command/Doctrine/CurrentCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['current']) 16 | ->setDescription('Outputs the current version') 17 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 18 | ; 19 | 20 | parent::configure(); 21 | } 22 | 23 | protected function execute(InputInterface $input, OutputInterface $output): int 24 | { 25 | $newInput = new ArrayInput([]); 26 | 27 | $newInput->setInteractive($input->isInteractive()); 28 | 29 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 30 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\CurrentCommand($dependencyFactory); 31 | $otherCommand->run($newInput, $output); 32 | } 33 | 34 | return self::SUCCESS; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Command/Doctrine/DiffCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Generate a migration by comparing your current database to your mapping information.') 16 | ->setHelp( 17 | <<%command.name% command generates a migration by comparing your current database to your mapping information: 19 | 20 | %command.full_name% 21 | 22 | EOT 23 | ) 24 | ->addOption( 25 | 'namespace', 26 | null, 27 | InputOption::VALUE_REQUIRED, 28 | 'The namespace to use for the migration (must be in the list of configured namespaces)' 29 | ) 30 | ->addOption( 31 | 'filter-expression', 32 | null, 33 | InputOption::VALUE_REQUIRED, 34 | 'Tables which are filtered by Regular Expression.' 35 | ) 36 | ->addOption( 37 | 'formatted', 38 | null, 39 | InputOption::VALUE_NONE, 40 | 'Format the generated SQL.' 41 | ) 42 | ->addOption( 43 | 'line-length', 44 | null, 45 | InputOption::VALUE_REQUIRED, 46 | 'Max line length of unformatted lines.', 47 | 120 48 | ) 49 | ->addOption( 50 | 'check-database-platform', 51 | null, 52 | InputOption::VALUE_OPTIONAL, 53 | 'Check Database Platform to the generated code.', 54 | false 55 | ) 56 | ->addOption( 57 | 'allow-empty-diff', 58 | null, 59 | InputOption::VALUE_NONE, 60 | 'Do not throw an exception when no changes are detected.' 61 | ) 62 | ->addOption( 63 | 'from-empty-schema', 64 | null, 65 | InputOption::VALUE_NONE, 66 | 'Generate a full migration as if the current database was empty.' 67 | ) 68 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 69 | ; 70 | } 71 | 72 | protected function execute(InputInterface $input, OutputInterface $output): int 73 | { 74 | $parameters = [ 75 | '--formatted' => $input->getOption('formatted'), 76 | '--line-length' => $input->getOption('line-length'), 77 | '--check-database-platform' => $input->getOption('check-database-platform'), 78 | '--allow-empty-diff' => $input->getOption('allow-empty-diff'), 79 | '--from-empty-schema' => $input->getOption('from-empty-schema'), 80 | ]; 81 | 82 | if ('' !== (string) $input->getOption('namespace')) { 83 | $parameters['--namespace'] = $input->getOption('namespace'); 84 | } 85 | 86 | if ('' !== (string) $input->getOption('filter-expression')) { 87 | $parameters['--filter-expression'] = $input->getOption('filter-expression'); 88 | } 89 | 90 | $newInput = new ArrayInput($parameters); 91 | 92 | $newInput->setInteractive($input->isInteractive()); 93 | 94 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 95 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\DiffCommand($dependencyFactory); 96 | $otherCommand->run($newInput, $output); 97 | } 98 | 99 | return self::SUCCESS; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Command/Doctrine/DumpSchemaCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['dump-schema']) 16 | ->setDescription('Dump the schema for your database to a migration.') 17 | ->setHelp( 18 | <<%command.name% command dumps the schema for your database to a migration: 20 | 21 | %command.full_name% 22 | 23 | After dumping your schema to a migration, you can rollup your migrations using the migrations:rollup command. 24 | EOT 25 | ) 26 | ->addOption( 27 | 'formatted', 28 | null, 29 | InputOption::VALUE_NONE, 30 | 'Format the generated SQL.' 31 | ) 32 | ->addOption( 33 | 'namespace', 34 | null, 35 | InputOption::VALUE_REQUIRED, 36 | 'Namespace to use for the generated migrations (defaults to the first namespace definition).' 37 | ) 38 | ->addOption( 39 | 'filter-tables', 40 | null, 41 | InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 42 | 'Filter the tables to dump via Regex.' 43 | ) 44 | ->addOption( 45 | 'line-length', 46 | null, 47 | InputOption::VALUE_OPTIONAL, 48 | 'Max line length of unformatted lines.', 49 | 120 50 | ) 51 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 52 | ; 53 | 54 | parent::configure(); 55 | } 56 | 57 | protected function execute(InputInterface $input, OutputInterface $output): int 58 | { 59 | $arguments = [ 60 | '--formatted' => $input->getOption('formatted'), 61 | '--filter-tables' => $input->getOption('filter-tables'), 62 | '--line-length' => $input->getOption('line-length'), 63 | ]; 64 | 65 | if (null !== $input->getOption('namespace')) { 66 | $arguments['--namespace'] = $input->getOption('namespace'); 67 | } 68 | 69 | $newInput = new ArrayInput($arguments); 70 | 71 | $newInput->setInteractive($input->isInteractive()); 72 | 73 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 74 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\DumpSchemaCommand($dependencyFactory); 75 | $otherCommand->run($newInput, $output); 76 | } 77 | 78 | return self::SUCCESS; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Command/Doctrine/ExecuteCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['execute']) 17 | ->setDescription( 18 | 'Execute one or more migration versions up or down manually.' 19 | ) 20 | ->addArgument( 21 | 'versions', 22 | InputArgument::REQUIRED | InputArgument::IS_ARRAY, 23 | 'The versions to execute.', 24 | null 25 | ) 26 | ->addOption( 27 | 'write-sql', 28 | null, 29 | InputOption::VALUE_OPTIONAL, 30 | 'The path to output the migration SQL file instead of executing it. Defaults to current working directory.', 31 | false 32 | ) 33 | ->addOption( 34 | 'dry-run', 35 | null, 36 | InputOption::VALUE_NONE, 37 | 'Execute the migration as a dry run.' 38 | ) 39 | ->addOption( 40 | 'up', 41 | null, 42 | InputOption::VALUE_NONE, 43 | 'Execute the migration up.' 44 | ) 45 | ->addOption( 46 | 'down', 47 | null, 48 | InputOption::VALUE_NONE, 49 | 'Execute the migration down.' 50 | ) 51 | ->addOption( 52 | 'query-time', 53 | null, 54 | InputOption::VALUE_NONE, 55 | 'Time all the queries individually.' 56 | ) 57 | ->setHelp( 58 | <<%command.name% command executes migration versions up or down manually: 60 | 61 | %command.full_name% FQCN 62 | 63 | If no --up or --down option is specified it defaults to up: 64 | 65 | %command.full_name% FQCN --down 66 | 67 | You can also execute the migration as a --dry-run: 68 | 69 | %command.full_name% FQCN --dry-run 70 | 71 | You can output the would be executed SQL statements to a file with --write-sql: 72 | 73 | %command.full_name% FQCN --write-sql 74 | 75 | Or you can also execute the migration without a warning message which you need to interact with: 76 | 77 | %command.full_name% FQCN --no-interaction 78 | 79 | All the previous commands accept multiple migration versions, allowing you run execute more than 80 | one migration at once: 81 | %command.full_name% FQCN-1 FQCN-2 ...FQCN-n 82 | 83 | EOT 84 | ) 85 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 86 | ; 87 | 88 | parent::configure(); 89 | } 90 | 91 | protected function execute(InputInterface $input, OutputInterface $output): int 92 | { 93 | $newInput = new ArrayInput([ 94 | 'versions' => $input->getArgument('versions'), 95 | '--write-sql' => $input->getOption('write-sql'), 96 | '--dry-run' => $input->getOption('dry-run'), 97 | '--up' => $input->getOption('up'), 98 | '--down' => $input->getOption('down'), 99 | '--query-time' => $input->getOption('query-time'), 100 | ]); 101 | 102 | $newInput->setInteractive($input->isInteractive()); 103 | 104 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 105 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\ExecuteCommand($dependencyFactory); 106 | $otherCommand->run($newInput, $output); 107 | } 108 | 109 | return self::SUCCESS; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Command/Doctrine/GenerateCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['generate']) 16 | ->setDescription('Generate a blank migration class.') 17 | ->addOption( 18 | 'namespace', 19 | null, 20 | InputOption::VALUE_REQUIRED, 21 | 'The namespace to use for the migration (must be in the list of configured namespaces)' 22 | ) 23 | ->setHelp( 24 | <<%command.name% command generates a blank migration class: 26 | 27 | %command.full_name% 28 | 29 | EOT 30 | ) 31 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 32 | ; 33 | 34 | parent::configure(); 35 | } 36 | 37 | protected function execute(InputInterface $input, OutputInterface $output): int 38 | { 39 | $parameters = []; 40 | 41 | if ('' !== (string) $input->getOption('namespace')) { 42 | $parameters['--namespace'] = $input->getOption('namespace'); 43 | } 44 | 45 | $newInput = new ArrayInput($parameters); 46 | 47 | $newInput->setInteractive($input->isInteractive()); 48 | 49 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 50 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\GenerateCommand($dependencyFactory); 51 | $otherCommand->run($newInput, $output); 52 | } 53 | 54 | return self::SUCCESS; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Command/Doctrine/LatestCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['latest']) 16 | ->setDescription('Outputs the latest version') 17 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 18 | ; 19 | 20 | parent::configure(); 21 | } 22 | 23 | protected function execute(InputInterface $input, OutputInterface $output): int 24 | { 25 | $newInput = new ArrayInput([]); 26 | 27 | $newInput->setInteractive($input->isInteractive()); 28 | 29 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 30 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\LatestCommand($dependencyFactory); 31 | $otherCommand->run($newInput, $output); 32 | } 33 | 34 | return self::SUCCESS; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Command/Doctrine/ListCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['list-migrations']) 16 | ->setDescription('Display a list of all available migrations and their status.') 17 | ->setHelp( 18 | <<%command.name% command outputs a list of all available migrations and their status: 20 | 21 | %command.full_name% 22 | EOT 23 | ) 24 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 25 | ; 26 | 27 | parent::configure(); 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output): int 31 | { 32 | $newInput = new ArrayInput([]); 33 | 34 | $newInput->setInteractive($input->isInteractive()); 35 | 36 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 37 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\CurrentCommand($dependencyFactory); 38 | $otherCommand->run($newInput, $output); 39 | } 40 | 41 | return self::SUCCESS; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Command/Doctrine/MigrateCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Execute a migration to a specified version or the latest available version.') 17 | ->addArgument( 18 | 'version', 19 | InputArgument::OPTIONAL, 20 | 'The version FQCN or alias (first, prev, next, latest) to migrate to.', 21 | 'latest' 22 | ) 23 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 24 | ->addOption( 25 | 'write-sql', 26 | null, 27 | InputOption::VALUE_OPTIONAL, 28 | 'The path to output the migration SQL file instead of executing it. Defaults to current working directory.', 29 | false 30 | ) 31 | ->addOption( 32 | 'dry-run', 33 | null, 34 | InputOption::VALUE_NONE, 35 | 'Execute the migration as a dry run.' 36 | ) 37 | ->addOption( 38 | 'query-time', 39 | null, 40 | InputOption::VALUE_NONE, 41 | 'Time all the queries individually.' 42 | ) 43 | ->addOption( 44 | 'allow-no-migration', 45 | null, 46 | InputOption::VALUE_NONE, 47 | 'Do not throw an exception if no migration is available.' 48 | ) 49 | ->addOption( 50 | 'all-or-nothing', 51 | null, 52 | InputOption::VALUE_OPTIONAL, 53 | 'Wrap the entire migration in a transaction.', 54 | false 55 | ) 56 | ; 57 | } 58 | 59 | protected function execute(InputInterface $input, OutputInterface $output): int 60 | { 61 | $newInput = new ArrayInput([ 62 | 'version' => $input->getArgument('version'), 63 | '--write-sql' => $input->getOption('write-sql'), 64 | '--dry-run' => $input->getOption('dry-run'), 65 | '--query-time' => $input->getOption('query-time'), 66 | '--allow-no-migration' => $input->getOption('allow-no-migration'), 67 | '--all-or-nothing' => $input->getOption('all-or-nothing'), 68 | ]); 69 | 70 | $newInput->setInteractive($input->isInteractive()); 71 | 72 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 73 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\MigrateCommand($dependencyFactory); 74 | $otherCommand->run($newInput, $output); 75 | } 76 | 77 | return self::SUCCESS; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Command/Doctrine/RollupCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['rollup']) 16 | ->setDescription('Rollup migrations by deleting all tracked versions and insert the one version that exists.') 17 | ->setHelp( 18 | <<%command.name% command rolls up migrations by deleting all tracked versions and 20 | inserts the one version that exists that was created with the migrations:dump-schema command. 21 | 22 | %command.full_name% 23 | 24 | To dump your schema to a migration version you can use the migrations:dump-schema command. 25 | EOT 26 | ) 27 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 28 | ; 29 | 30 | parent::configure(); 31 | } 32 | 33 | protected function execute(InputInterface $input, OutputInterface $output): int 34 | { 35 | $newInput = new ArrayInput([]); 36 | 37 | $newInput->setInteractive($input->isInteractive()); 38 | 39 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 40 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\RollupCommand($dependencyFactory); 41 | $otherCommand->run($newInput, $output); 42 | } 43 | 44 | return self::SUCCESS; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Command/Doctrine/StatusCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['status']) 16 | ->setDescription('View the status of a set of migrations.') 17 | ->setHelp( 18 | <<%command.name% command outputs the status of a set of migrations: 20 | 21 | %command.full_name% 22 | EOT 23 | ) 24 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 25 | ; 26 | 27 | parent::configure(); 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output): int 31 | { 32 | $newInput = new ArrayInput([]); 33 | 34 | $newInput->setInteractive($input->isInteractive()); 35 | 36 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 37 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\StatusCommand($dependencyFactory); 38 | $otherCommand->run($newInput, $output); 39 | } 40 | 41 | return self::SUCCESS; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Command/Doctrine/SyncMetadataCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['sync-metadata-storage']) 18 | ->setDescription('Ensures that the metadata storage is at the latest version.') 19 | ->setHelp( 20 | <<%command.name% command updates metadata storage the latest version. 22 | 23 | %command.full_name% 24 | EOT 25 | ) 26 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 27 | ; 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output): int 31 | { 32 | $newInput = new ArrayInput([]); 33 | 34 | $newInput->setInteractive($input->isInteractive()); 35 | 36 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 37 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\SyncMetadataCommand($dependencyFactory); 38 | $otherCommand->run($newInput, $output); 39 | } 40 | 41 | return self::SUCCESS; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Command/Doctrine/UpToDateCommand.php: -------------------------------------------------------------------------------- 1 | setAliases(['up-to-date']) 16 | ->setDescription('Tells you if your schema is up-to-date.') 17 | ->addOption('fail-on-unregistered', 'u', InputOption::VALUE_NONE, 'Whether to fail when there are unregistered extra migrations found') 18 | ->addOption('list-migrations', 'l', InputOption::VALUE_NONE, 'Show a list of missing or not migrated versions.') 19 | ->setHelp( 20 | <<%command.name% command tells you if your schema is up-to-date: 22 | 23 | %command.full_name% 24 | EOT 25 | ) 26 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 27 | ; 28 | 29 | parent::configure(); 30 | } 31 | 32 | protected function execute(InputInterface $input, OutputInterface $output): int 33 | { 34 | $newInput = new ArrayInput([ 35 | '--fail-on-unregistered' => $input->getOption('fail-on-unregistered'), 36 | '--list-migrations' => $input->getOption('list-migrations'), 37 | ]); 38 | 39 | $newInput->setInteractive($input->isInteractive()); 40 | 41 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 42 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\UpToDateCommand($dependencyFactory); 43 | $otherCommand->run($newInput, $output); 44 | } 45 | 46 | return self::SUCCESS; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Command/Doctrine/VersionCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Manually add and delete migration versions from the version table.') 17 | ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the Entity Manager to handle.') 18 | ->addArgument( 19 | 'version', 20 | InputArgument::OPTIONAL, 21 | 'The version to add or delete.', 22 | null 23 | ) 24 | ->addOption( 25 | 'add', 26 | null, 27 | InputOption::VALUE_NONE, 28 | 'Add the specified version.' 29 | ) 30 | ->addOption( 31 | 'delete', 32 | null, 33 | InputOption::VALUE_NONE, 34 | 'Delete the specified version.' 35 | ) 36 | ->addOption( 37 | 'all', 38 | null, 39 | InputOption::VALUE_NONE, 40 | 'Apply to all the versions.' 41 | ) 42 | ->addOption( 43 | 'range-from', 44 | null, 45 | InputOption::VALUE_OPTIONAL, 46 | 'Apply from specified version.' 47 | ) 48 | ->addOption( 49 | 'range-to', 50 | null, 51 | InputOption::VALUE_OPTIONAL, 52 | 'Apply to specified version.' 53 | ) 54 | ->setHelp( 55 | <<%command.name% command allows you to manually add, delete or synchronize migration versions from the version table: 57 | 58 | %command.full_name% MIGRATION-FQCN --add 59 | 60 | If you want to delete a version you can use the --delete option: 61 | 62 | %command.full_name% MIGRATION-FQCN --delete 63 | 64 | If you want to synchronize by adding or deleting all migration versions available in the version table you can use the --all option: 65 | 66 | %command.full_name% --add --all 67 | %command.full_name% --delete --all 68 | 69 | If you want to synchronize by adding or deleting some range of migration versions available in the version table you can use the --range-from/--range-to option: 70 | 71 | %command.full_name% --add --range-from=MIGRATION-FQCN --range-to=MIGRATION-FQCN 72 | %command.full_name% --delete --range-from=MIGRATION-FQCN --range-to=MIGRATION-FQCN 73 | 74 | You can also execute this command without a warning message which you need to interact with: 75 | 76 | %command.full_name% --no-interaction 77 | EOT 78 | ); 79 | } 80 | 81 | protected function execute(InputInterface $input, OutputInterface $output): int 82 | { 83 | $newInput = new ArrayInput([ 84 | 'version' => $input->getArgument('version'), 85 | '--add' => $input->getOption('add'), 86 | '--delete' => $input->getOption('delete'), 87 | '--all' => $input->getOption('all'), 88 | '--range-from' => $input->getOption('range-from'), 89 | '--range-to' => $input->getOption('range-to'), 90 | ]); 91 | 92 | $newInput->setInteractive($input->isInteractive()); 93 | 94 | foreach ($this->getDependencyFactories(strval($input->getOption('em'))) as $dependencyFactory) { 95 | $otherCommand = new \Doctrine\Migrations\Tools\Console\Command\VersionCommand($dependencyFactory); 96 | $otherCommand->run($newInput, $output); 97 | } 98 | 99 | return self::SUCCESS; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Configuration/Configuration.php: -------------------------------------------------------------------------------- 1 | dependencyFactories[$name] = $entityManager; 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * @return DependencyFactory[] 21 | */ 22 | public function getDependencyFactories(): array 23 | { 24 | return $this->dependencyFactories; 25 | } 26 | 27 | /** 28 | * @return string[] 29 | */ 30 | public function getEntityManagerNames(): array 31 | { 32 | return array_keys($this->dependencyFactories); 33 | } 34 | 35 | public function getConfigurationByEntityManagerName(string $name): ?DependencyFactory 36 | { 37 | if (array_key_exists($name, $this->dependencyFactories)) { 38 | return $this->dependencyFactories[$name]; 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode(); 25 | 26 | $organizeMigrationModes = $this->getOrganizeMigrationsModes(); 27 | 28 | $rootNode 29 | ->children() 30 | ->arrayNode('entity_managers') 31 | ->arrayPrototype() 32 | ->fixXmlConfig('migration', 'migrations') 33 | ->fixXmlConfig('migrations_path', 'migrations_paths') 34 | ->children() 35 | ->arrayNode('migrations_paths') 36 | ->info('A list of namespace/path pairs where to look for migrations.') 37 | ->defaultValue([]) 38 | ->useAttributeAsKey('namespace') 39 | ->prototype('scalar')->end() 40 | ->end() 41 | 42 | ->arrayNode('services') 43 | ->info('A set of services to pass to the underlying doctrine/migrations library, allowing to change its behaviour.') 44 | ->useAttributeAsKey('service') 45 | ->defaultValue([]) 46 | ->validate() 47 | ->ifTrue(static function ($v) { 48 | return count(array_filter(array_keys($v), static function (string $doctrineService): bool { 49 | return 0 !== strpos($doctrineService, 'Doctrine\Migrations\\'); 50 | })); 51 | }) 52 | ->thenInvalid('Valid services for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.') 53 | ->end() 54 | ->prototype('scalar')->end() 55 | ->end() 56 | 57 | ->arrayNode('factories') 58 | ->info('A set of callables to pass to the underlying doctrine/migrations library as services, allowing to change its behaviour.') 59 | ->useAttributeAsKey('factory') 60 | ->defaultValue([]) 61 | ->validate() 62 | ->ifTrue(static function ($v) { 63 | return count(array_filter(array_keys($v), static function (string $doctrineService): bool { 64 | return 0 !== strpos($doctrineService, 'Doctrine\Migrations\\'); 65 | })); 66 | }) 67 | ->thenInvalid('Valid callables for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.') 68 | ->end() 69 | ->prototype('scalar')->end() 70 | ->end() 71 | 72 | ->arrayNode('storage') 73 | ->addDefaultsIfNotSet() 74 | ->info('Storage to use for migration status metadata.') 75 | ->children() 76 | ->arrayNode('table_storage') 77 | ->addDefaultsIfNotSet() 78 | ->info('The default metadata storage, implemented as a table in the database.') 79 | ->children() 80 | ->scalarNode('table_name')->defaultValue(null)->cannotBeEmpty()->end() 81 | ->scalarNode('version_column_name')->defaultValue(null)->end() 82 | ->scalarNode('version_column_length')->defaultValue(null)->end() 83 | ->scalarNode('executed_at_column_name')->defaultValue(null)->end() 84 | ->scalarNode('execution_time_column_name')->defaultValue(null)->end() 85 | ->end() 86 | ->end() 87 | ->end() 88 | ->end() 89 | 90 | ->arrayNode('migrations') 91 | ->info('A list of migrations to load in addition to the one discovered via "migrations_paths".') 92 | ->prototype('scalar')->end() 93 | ->defaultValue([]) 94 | ->end() 95 | ->scalarNode('all_or_nothing') 96 | ->info('Run all migrations in a transaction.') 97 | ->defaultValue(false) 98 | ->end() 99 | ->scalarNode('check_database_platform') 100 | ->info('Adds an extra check in the generated migrations to allow execution only on the same platform as they were initially generated on.') 101 | ->defaultValue(true) 102 | ->end() 103 | ->scalarNode('custom_template') 104 | ->info('Custom template path for generated migration classes.') 105 | ->defaultValue(null) 106 | ->end() 107 | ->scalarNode('organize_migrations') 108 | ->defaultValue(false) 109 | ->info('Organize migrations mode. Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false') 110 | ->validate() 111 | ->ifTrue(static function ($v) use ($organizeMigrationModes): bool { 112 | if (false === $v) { 113 | return false; 114 | } 115 | 116 | if (is_string($v) && in_array(strtoupper($v), $organizeMigrationModes, true)) { 117 | return false; 118 | } 119 | 120 | return true; 121 | }) 122 | ->thenInvalid('Invalid organize migrations mode value %s') 123 | ->end() 124 | ->validate() 125 | ->ifString() 126 | ->then(static function ($v) { 127 | return constant('Doctrine\Migrations\Configuration\Configuration::VERSIONS_ORGANIZATION_'.strtoupper($v)); 128 | }) 129 | ->end() 130 | ->end() 131 | ->end() 132 | ->end() 133 | ->end() 134 | ; 135 | 136 | return $treeBuilder; 137 | } 138 | 139 | /** 140 | * Find organize migrations modes for their names. 141 | * 142 | * @return string[] 143 | */ 144 | private function getOrganizeMigrationsModes(): array 145 | { 146 | $constPrefix = 'VERSIONS_ORGANIZATION_'; 147 | $prefixLen = strlen($constPrefix); 148 | $refClass = new ReflectionClass('Doctrine\Migrations\Configuration\Configuration'); 149 | $constsArray = $refClass->getConstants(); 150 | $namesArray = []; 151 | 152 | foreach ($constsArray as $key => $value) { 153 | if (0 !== strpos($key, $constPrefix)) { 154 | continue; 155 | } 156 | 157 | $namesArray[] = substr($key, $prefixLen); 158 | } 159 | 160 | return $namesArray; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/DependencyInjection/DoctrineMigrationsMultipleDatabaseExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 29 | 30 | $locator = new FileLocator(__DIR__.'/../Resources/config/'); 31 | $loader = new YamlFileLoader($container, $locator); 32 | 33 | $loader->load('services.yaml'); 34 | 35 | foreach ($config['entity_managers'] as $name => $connection) { 36 | $this->loadEntityManagerConfiguration($name, $connection, $container); 37 | } 38 | } 39 | 40 | /** 41 | * @param mixed[] $connection 42 | */ 43 | private function loadEntityManagerConfiguration(string $name, array $connection, ContainerBuilder $container): void 44 | { 45 | $configuration = $container->setDefinition(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.configuration', $name), new ChildDefinition('doctrine.migrations_multiple_database.connection_configuration')); 46 | $container 47 | ->register(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.configuration_loader', $name), ExistingConfiguration::class) 48 | ->addArgument(new Reference(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.configuration', $name))); 49 | 50 | $container 51 | ->register(sprintf('doctrine.migrations_multiple_database.%s_em_loader', $name), ExistingEntityManager::class) 52 | ->addArgument(new Reference(sprintf('doctrine.orm.%s_entity_manager', $name))); 53 | 54 | $diDefinition = $container->setDefinition(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.dependency_factory', $name), new ChildDefinition('doctrine.migrations_multiple_database.dependency_factory')); 55 | $diDefinition 56 | ->setFactory([DependencyFactory::class, 'fromEntityManager']) 57 | ->setArgument(0, new Reference(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.configuration_loader', $name))) 58 | ->setArgument(1, new Reference(sprintf('doctrine.migrations_multiple_database.%s_em_loader', $name))); 59 | 60 | foreach ($connection['migrations_paths'] as $migrationNamespace => $migrationPath) { 61 | $migrationDirectory = $this->checkIfBundleRelativePath($migrationPath, $container); 62 | $configuration->addMethodCall('addMigrationsDirectory', [$migrationNamespace, $migrationDirectory]); 63 | } 64 | 65 | foreach ($connection['migrations'] as $migrationClass) { 66 | $configuration->addMethodCall('addMigrationClass', [$migrationClass]); 67 | } 68 | 69 | if (false !== $connection['organize_migrations']) { 70 | $configuration->addMethodCall('setMigrationOrganization', [$connection['organize_migrations']]); 71 | } 72 | 73 | if (null !== $connection['custom_template']) { 74 | $configuration->addMethodCall('setCustomTemplate', [$connection['custom_template']]); 75 | } 76 | 77 | $configuration->addMethodCall('setAllOrNothing', [$connection['all_or_nothing']]); 78 | $configuration->addMethodCall('setCheckDatabasePlatform', [$connection['check_database_platform']]); 79 | 80 | $container 81 | ->getDefinition('doctrine.migrations_multiple_database.configuration') 82 | ->addMethodCall('addDependencyFactory', [$name, new Reference(sprintf('doctrine.migrations_multiple_database.%s_entity_manager.dependency_factory', $name))]); 83 | 84 | foreach ($connection['services'] as $doctrineId => $symfonyId) { 85 | $diDefinition->addMethodCall('setDefinition', [$doctrineId, new ServiceClosureArgument(new Reference($symfonyId))]); 86 | } 87 | 88 | foreach ($connection['factories'] as $doctrineId => $symfonyId) { 89 | $diDefinition->addMethodCall('setDefinition', [$doctrineId, new Reference($symfonyId)]); 90 | } 91 | 92 | if (!isset($connection['services'][MetadataStorage::class])) { 93 | $storageConfiguration = $connection['storage']['table_storage']; 94 | 95 | $storageDefinition = new Definition(TableMetadataStorageConfiguration::class); 96 | $container->setDefinition(sprintf('doctrine.migrations_multiple_database.storage.%s_table_storage', $name), $storageDefinition); 97 | $container->setAlias('doctrine.migrations_multiple_database.storage.%s_metadata_storage', 'doctrine.migrations_multiple_database.storage.%s_table_storage'); 98 | 99 | if (null !== $storageConfiguration['table_name']) { 100 | $storageDefinition->addMethodCall('setTableName', [$storageConfiguration['table_name']]); 101 | } 102 | if (null !== $storageConfiguration['version_column_name']) { 103 | $storageDefinition->addMethodCall('setVersionColumnName', [$storageConfiguration['version_column_name']]); 104 | } 105 | if (null !== $storageConfiguration['version_column_length']) { 106 | $storageDefinition->addMethodCall('setVersionColumnLength', [$storageConfiguration['version_column_length']]); 107 | } 108 | if (null !== $storageConfiguration['executed_at_column_name']) { 109 | $storageDefinition->addMethodCall('setExecutedAtColumnName', [$storageConfiguration['executed_at_column_name']]); 110 | } 111 | if (null !== $storageConfiguration['execution_time_column_name']) { 112 | $storageDefinition->addMethodCall('setExecutionTimeColumnName', [$storageConfiguration['execution_time_column_name']]); 113 | } 114 | 115 | $configuration->addMethodCall('setMetadataStorageConfiguration', [new Reference(sprintf('doctrine.migrations_multiple_database.storage.%s_table_storage', $name))]); 116 | } 117 | } 118 | 119 | private function checkIfBundleRelativePath(string $path, ContainerBuilder $container): string 120 | { 121 | if (isset($path[0]) && '@' === $path[0]) { 122 | $pathParts = explode('/', $path); 123 | $bundleName = substr($pathParts[0], 1); 124 | 125 | $bundlePath = $this->getBundlePath($bundleName, $container); 126 | 127 | return $bundlePath.substr($path, strlen('@'.$bundleName)); 128 | } 129 | 130 | return $path; 131 | } 132 | 133 | private function getBundlePath(string $bundleName, ContainerBuilder $container): string 134 | { 135 | $bundleMetadata = $container->getParameter('kernel.bundles_metadata'); 136 | 137 | if (!isset($bundleMetadata[$bundleName])) { 138 | throw new RuntimeException(sprintf('The bundle "%s" has not been registered, available bundles: %s', $bundleName, implode(', ', array_keys($bundleMetadata)))); 139 | } 140 | 141 | return $bundleMetadata[$bundleName]['path']; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/DoctrineMigrationsMultipleDatabaseBundle.php: -------------------------------------------------------------------------------- 1 |