From 57fb237fa1c5f4b3aa687c7d6b5066ab4793f3be Mon Sep 17 00:00:00 2001 From: Vladimir Tsykun Date: Sun, 17 Mar 2019 19:49:58 +0300 Subject: [PATCH] Migration make command - remove twig dependencies and make it is optional --- README.md | 73 ++++++-- composer.json | 6 +- src/Command/DiffMigrationsCommand.php | 177 ++++++++++++++---- src/Command/DumpMigrationsCommand.php | 40 +++- src/DependencyInjection/Configuration.php | 2 +- src/Resources/config/services.yml | 4 +- .../views/schema-diff-template.php.twig | 27 ++- src/Resources/views/schema-template.php.twig | 11 +- src/Tools/SchemaDiffDumper.php | 11 +- src/Tools/SchemaDumper.php | 11 +- 10 files changed, 272 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 4c7b2a4..70cf261 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,55 @@ Database structure and data manipulator. [![Latest Unstable Version](https://poser.okvpn.org/okvpn/migration-bundle/v/unstable)](//packagist.org/packages/okvpn/migration-bundle) [![License](https://poser.okvpn.org/okvpn/migration-bundle/license)](https://packagist.org/packages/okvpn/migration-bundle) -Purpose +![Migration cast](http://poser.okvpn.org/images/migrationcast.svg) + +Intro ------- -MigrationBundle is subtree split of the [OroMigrationBundle](https://github.com/orocrm/platform/tree/master/src/Oro/Bundle/MigrationBundle). -The ORO developers made a great tool, but it has very strong dependencies on other ORO Platform bundles (like as [EmailBundle, MessageQueue, SearchBundle](https://github.com/orocrm/platform/blob/2.1/src/Oro/Bundle/MigrationBundle/Command/LoadDataFixturesCommand.php#L13-L17) etc.). So we forked this bundle and removed BAP dependencies, that makes this bundle more reusable in non-Oro Platform projects + +OkvpnMigrationBundle allow write database migrations using database agnostic PHP code, +which uses the external [doctrine/dbal][7] library Doctrine Schema Manager. + +```php +createTable('meteo'); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('timestamp', 'datetime'); + $table->addColumn('temp', 'decimal', ['precision' => 4, 'scale' => 2]); + $table->addColumn('pressure', 'decimal', ['precision' => 6, 'scale' => 2]); + $table->addColumn('humidity', 'decimal', ['precision' => 4, 'scale' => 2]); + $table->setPrimaryKey(['id']); + $table->addIndex(['timestamp']); + } +} +``` Features -------- - Write database migrations using database agnostic PHP code. -- Locate migrations inside each bundle. -- Compatible with different versions of Doctrine and Symfony. +- Locate migrations inside each bundle and supports the multiple locations. +- Compatible with different versions of Doctrine and Symfony 3-4. - Extensions for database structure migrations. - Events before and after migrations. @@ -28,27 +67,32 @@ Install using composer: composer require okvpn/migration-bundle ``` -And add this bundle to your AppKernel: +If you don't use Symfony Flex, you must enable the bundle manually in the application: +Symfony 4 `config/bundles.php` +```php + ['all' => true], + //... +]; + +``` + +Symfony 2 - 3, enable the bundle in `app/AppKernel.php` ```php load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); + ]; } } ``` @@ -311,3 +355,4 @@ Also if you need to use other extension in your extension the extension class sh [4]: ./Migration/Extension/NameGeneratorAwareInterface.php [5]: ./Migration/Extension/RenameExtension.php [6]: ./Migration/Extension/RenameExtensionAwareInterface.php + [7]: https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/schema-manager.html diff --git a/composer.json b/composer.json index d1e5c9e..79527ee 100644 --- a/composer.json +++ b/composer.json @@ -15,19 +15,19 @@ "php": ">=7.0", "symfony/framework-bundle": "~2.7|~3.0|~4.0", "symfony/doctrine-bridge": "~2.7|~3.0|~4.0", - "symfony/twig-bundle": "~2.7|~3.0|~4.0", "doctrine/dbal": "^2.1", "doctrine/orm": "^2.1" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7", + "symfony/twig-bundle": "~3.4|~4.2" }, "autoload": { "psr-4": { "Okvpn\\Bundle\\MigrationBundle\\": "src" } }, "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.1-dev" } } } diff --git a/src/Command/DiffMigrationsCommand.php b/src/Command/DiffMigrationsCommand.php index d55f9c0..af83314 100644 --- a/src/Command/DiffMigrationsCommand.php +++ b/src/Command/DiffMigrationsCommand.php @@ -1,5 +1,7 @@ setName('okvpn:migration:diff') ->addOption('plain-sql', null, InputOption::VALUE_NONE, 'Out schema as plain sql queries') - ->addOption( - 'bundle', - null, - InputOption::VALUE_REQUIRED, - 'Bundle name for which migration wll be generated' - ) - ->addOption( - 'migration-version', - null, - InputOption::VALUE_OPTIONAL, - 'Migration version', - 'v1_0' - ) + ->addOption('bundle', null, InputOption::VALUE_REQUIRED, 'Bundle name for which migration wll be generated') + ->addOption('migration-version', null, InputOption::VALUE_OPTIONAL, 'Migration version, for example v1_0') + ->addOption('write', null, InputOption::VALUE_NONE, 'Write migration to your filesystem') + ->addOption('entity', null, InputOption::VALUE_OPTIONAL, 'Dump migration only for this entity, for example: \'App\\\\Bundle\\\\User*\', \'^App\\\\(.*)\\\\Region$\'') ->setDescription('Compare current existing database structure with orm structure'); } @@ -88,9 +88,9 @@ protected function configure() */ protected function interact(InputInterface $input, OutputInterface $output) { - if (!$input->getOption('bundle')) { + if (!$input->getOption('bundle') && !$input->getOption('entity')) { $helper = $this->getHelper('question'); - $question = new Question("Please select your package name:\n > "); + $question = new Question("Please select your package/bundle name:\n > "); $bundleNames = array_keys($this->getContainer()->get('okvpn_migration.migrations.loader')->getBundleList()); $question->setAutocompleterValues($bundleNames); $question->setValidator(function ($answer) use ($bundleNames) { @@ -111,12 +111,14 @@ protected function interact(InputInterface $input, OutputInterface $output) */ public function execute(InputInterface $input, OutputInterface $output) { - if (!$input->getOption('bundle')) { - throw new \InvalidArgumentException('The "bundle" option can not be empty'); + if (!$input->getOption('bundle') && !$input->getOption('entity')) { + throw new \InvalidArgumentException('The "bundle" or "entity" option can not be empty'); } - $this->version = $input->getOption('migration-version'); $this->initializeBundleRestrictions($input->getOption('bundle')); + $this->initializeEntityRestrictions($input->getOption('entity')); + $this->version = $input->getOption('migration-version') ?: $this->getNextMigrationVersion(); + $this->initializeMetadataInformation(); $doctrine = $this->getContainer()->get('doctrine'); $connection = $doctrine->getConnection(); @@ -134,7 +136,7 @@ public function execute(InputInterface $input, OutputInterface $output) $output->writeln($sql . ';'); } } else { - $this->dumpPhpSchema($schemaDiff, $output); + $this->dumpPhpSchema($schemaDiff, $output, $input->getOption('write')); } } @@ -167,7 +169,7 @@ protected function getOkvpnSchemaProvider() /** * @param string $bundle */ - protected function initializeBundleRestrictions($bundle) + protected function initializeBundleRestrictions(?string $bundle) { if ($bundle) { $bundles = $this->getContainer()->get('okvpn_migration.migrations.loader')->getBundleList(); @@ -178,11 +180,60 @@ protected function initializeBundleRestrictions($bundle) } $this->migrationPath = $bundles[$bundle]['dir_name']; - $this->className = $bundle . 'Installer'; + $this->className = $bundle; $this->namespace = $bundles[$bundle]['namespace']; } } + /** + * @param string $entity + */ + protected function initializeEntityRestrictions(?string $entity) + { + if ($entity) { + $doctrine = $this->getContainer()->get('doctrine'); + /** @var ClassMetadataInfo[] $entities */ + $entities = array_filter( + $doctrine->getManager()->getMetadataFactory()->getAllMetadata(), + function (ClassMetadataInfo $item) use ($entity) { + /** @var ClassMetadataInfo $item */ + return preg_match('/' . str_replace('\\', '\\\\', $entity) . '/', $item->getName()); + } + ); + + if (!$entities) { + throw new \InvalidArgumentException(sprintf('Entity "%s" is not a known.', $entity)); + } + + $packages = array_filter( + $this->getContainer()->get('okvpn_migration.migrations.loader')->getBundleList(), + function (array $package) use ($entities) { + foreach ($entities as $entity) { + if (strpos($entity->getReflectionClass()->getFileName(), $package['dir_name']) !== 0) { + return false; + } + } + + return true; + } + ); + + if (!$packages) { + throw new \InvalidArgumentException(sprintf('Not found package for this entity "%s".', $entity)); + } + + $this->entities = array_map(function (ClassMetadataInfo $info) { + return $info->getName(); + }, $entities); + + foreach ($packages as $packageName => $package) { + $this->migrationPath = $package['dir_name']; + $this->className = $packageName; + $this->namespace = $package['namespace']; + } + } + } + /** * Process metadata information. */ @@ -196,6 +247,10 @@ protected function initializeMetadataInformation() function (ClassMetadata $entityMetadata) { if ($this->migrationPath) { if (strpos($entityMetadata->getReflectionClass()->getFileName(), $this->migrationPath) === 0) { + if ($this->entities && !in_array($entityMetadata->getName(), $this->entities)) { + return; + } + $this->allowedTables[$entityMetadata->getTableName()] = true; foreach ($entityMetadata->getAssociationMappings() as $associationMappingInfo) { if (!empty($associationMappingInfo['joinTable'])) { @@ -212,32 +267,52 @@ function (ClassMetadata $entityMetadata) { /** * @param SchemaDiff $schema * @param OutputInterface $output + * @param boolean $write */ - protected function dumpPhpSchema(SchemaDiff $schema, OutputInterface $output) + protected function dumpPhpSchema(SchemaDiff $schema, OutputInterface $output, $write = false) { $visitor = $this->getContainer()->get('okvpn_migration.tools.schema_diff_dumper'); $visitor->acceptSchemaDiff($schema); - - $output->writeln( - $visitor->dump( - $this->allowedTables, - $this->namespace, - $this->className, - $this->version, - $this->extendedFieldOptions - ) + $className = strpos($this->className, 'Bundle') + ? $this->className : $this->className . self::MIGRATION_CLASS_PREFIX; + + $code = $visitor->dump( + $this->allowedTables, + $this->namespace, + $className, + $this->version, + $this->extendedFieldOptions ); + + if ($write === true) { + $migrationPrefix = trim(preg_replace('/\//', '\\', $this->getContainer()->getParameter('okvpn.migrations_path')), "\\"); + $targetPath = $this->migrationPath . DIRECTORY_SEPARATOR + . str_replace('\\', DIRECTORY_SEPARATOR, $migrationPrefix .'\\' . $this->version); + if (!is_dir($targetPath)) { + @mkdir($targetPath, 0777, true); + } + + $output->writeln(' Using migration path ' . $targetPath . ''); + $output->writeln(' Using version ' . $this->version . ''); + $filename = $targetPath . DIRECTORY_SEPARATOR . $className . '.php'; + if (file_exists($filename)) { + throw new \RuntimeException('Migration ' . $filename . ' is exists, try to specify migration-version manually'); + } + + file_put_contents($filename, $code); + $output->writeln(' Write to file ' . $filename . ''); + } else { + $output->writeln($code); + } } /** * @param SchemaDiff $schemaDiff */ - private function removeExcludedTables(SchemaDiff $schemaDiff) + protected function removeExcludedTables(SchemaDiff $schemaDiff) { - $excludes = [ - 'okvpn_migrations', - ]; + $excludes = ['okvpn_migrations']; /** @var Table $v */ foreach ($schemaDiff->newTables as $k => $v) { @@ -253,4 +328,34 @@ private function removeExcludedTables(SchemaDiff $schemaDiff) } } } + + protected function getNextMigrationVersion() + { + $migrations = $this->getContainer()->get('okvpn_migration.migrations.loader')->getPlainMigrations(); + $version = null; + foreach ($migrations as $migration) { + if (!$migration->getVersion() || $migration->getBundleName() !== $this->className) { + continue; + } + + if (null !== $version) { + if (version_compare($version, $migration->getVersion()) === -1) { + $version = $migration->getVersion(); + } + } else { + $version = $migration->getVersion(); + } + } + + if ($version === null) { + return 'v1_0'; + } + + if (preg_match('/(\d+)$/', $version, $match)) { + $next = $match[1] + 1; + return preg_replace('/\d+$/', $next, $version); + } + + return $version; + } } diff --git a/src/Command/DumpMigrationsCommand.php b/src/Command/DumpMigrationsCommand.php index fbc1a26..c4d4bbd 100644 --- a/src/Command/DumpMigrationsCommand.php +++ b/src/Command/DumpMigrationsCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; class DumpMigrationsCommand extends ContainerAwareCommand { @@ -33,6 +34,11 @@ class DumpMigrationsCommand extends ContainerAwareCommand */ protected $className; + /** + * @var string + */ + protected $migrationPath; + /** * @var string */ @@ -61,6 +67,29 @@ protected function configure() ->setDescription('Dump existing database structure.'); } + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + if (!$input->getOption('bundle')) { + $helper = $this->getHelper('question'); + $question = new Question("Please select your package/bundle name:\n > "); + $bundleNames = array_keys($this->getContainer()->get('okvpn_migration.migrations.loader')->getBundleList()); + $question->setAutocompleterValues($bundleNames); + $question->setValidator(function ($answer) use ($bundleNames) { + if (!in_array($answer, $bundleNames)) { + throw new \RuntimeException(sprintf('Package "%s" does not exist.', $answer)); + } + return $answer; + }); + + $question->setMaxAttempts(3); + $bundle = $helper->ask($input, $output, $question); + $input->setOption('bundle', $bundle); + } + } + /** * {@inheritdoc} */ @@ -91,14 +120,16 @@ public function execute(InputInterface $input, OutputInterface $output) protected function initializeBundleRestrictions($bundle) { if ($bundle) { - $bundles = $this->getContainer()->getParameter('kernel.bundles'); + $bundles = $this->getContainer()->get('okvpn_migration.migrations.loader')->getBundleList(); if (!array_key_exists($bundle, $bundles)) { throw new \InvalidArgumentException( sprintf('Bundle "%s" is not a known bundle', $bundle) ); } - $this->namespace = str_replace($bundle, 'Entity', $bundles[$bundle]); + + $this->namespace = $bundles[$bundle]['namespace']; $this->className = $bundle . 'Installer'; + $this->migrationPath = $bundles[$bundle]['dir_name']; } } @@ -114,7 +145,7 @@ protected function initializeMetadataInformation() $allMetadata, function (ClassMetadata $entityMetadata) { if ($this->namespace) { - if ($entityMetadata->namespace == $this->namespace) { + if (strpos($entityMetadata->getReflectionClass()->getFileName(), $this->migrationPath) === 0) { $this->allowedTables[$entityMetadata->getTableName()] = true; foreach ($entityMetadata->getAssociationMappings() as $associationMappingInfo) { if (!empty($associationMappingInfo['joinTable'])) { @@ -122,10 +153,7 @@ function (ClassMetadata $entityMetadata) { $this->allowedTables[$joinTableName] = true; } } - //$this->initializeExtendedFieldsOptions($entityMetadata); } - } else { - //$this->initializeExtendedFieldsOptions($entityMetadata); } } ); diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 201dfda..7b6088a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -34,7 +34,7 @@ public function getConfigTreeBuilder() ->arrayNode('migrations_paths') ->info('Lookup migrations directories') ->useAttributeAsKey('name') - ->prototype('array') + ->arrayPrototype() ->children() ->scalarNode('dir_name')->defaultNull()->end() ->scalarNode('namespace') diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 95bb933..8a0c843 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -65,14 +65,14 @@ services: class: Okvpn\Bundle\MigrationBundle\Tools\SchemaDumper public: true arguments: - - '@twig' + - '@?twig' - '%okvpn.migrations_path%' okvpn_migration.tools.schema_diff_dumper: class: Okvpn\Bundle\MigrationBundle\Tools\SchemaDiffDumper public: true arguments: - - '@twig' + - '@?twig' - '%okvpn.migrations_path%' okvpn_migration.twig.schema_dumper: diff --git a/src/Resources/views/schema-diff-template.php.twig b/src/Resources/views/schema-diff-template.php.twig index 0e2d396..b840545 100644 --- a/src/Resources/views/schema-diff-template.php.twig +++ b/src/Resources/views/schema-diff-template.php.twig @@ -7,20 +7,8 @@ use Doctrine\DBAL\Schema\Schema; use Okvpn\Bundle\MigrationBundle\Migration\Migration; use Okvpn\Bundle\MigrationBundle\Migration\QueryBag; -/** - * @SuppressWarnings(PHPMD.TooManyMethods) - * @SuppressWarnings(PHPMD.ExcessiveClassLength) - */ class {{ className }} implements Migration { - /** - * {@inheritdoc} - */ - public function getMigrationVersion() - { - return '{{ version }}'; - } - /** * {@inheritdoc} */ @@ -68,7 +56,8 @@ class {{ className }} implements Migration $table = $schema->createTable('{{ table.name }}'); {% for column in table.columns %} {% set columnExtendedOptions = extendedOptions[table.name][column.name] is defined ? extendedOptions[table.name][column.name] : null %} - $table->addColumn('{{ column.name }}', '{{ column.type.name }}', {{ dmpr.dumpColumnOptions(column, columnExtendedOptions) }}); + {%- set dumpColumnOptions = dmpr.dumpColumnOptions(column, columnExtendedOptions) %} + $table->addColumn('{{ column.name }}', '{{ column.type.name }}'{{ (dumpColumnOptions ? ', ' ~ dumpColumnOptions : '')|raw }}); {% endfor %} {% for index in table.indexes %} {% if index.isPrimary %} @@ -134,11 +123,15 @@ class {{ className }} implements Migration { $table = $schema->getTable('{{ table.name }}'); {% for foreignKey in table.ForeignKeys %} +{% set optionsArray = dmpr.dumpOptionsArray(foreignKey.options) %} $table->addForeignKeyConstraint( $schema->getTable('{{ foreignKey.foreignTableName }}'), {{ dmpr.dumpArray(foreignKey.localColumns) }}, - {{ dmpr.dumpArray(foreignKey.foreignColumns) }}, - {{ dmpr.dumpOptionsArray(foreignKey.options) }} + {{ dmpr.dumpArray(foreignKey.foreignColumns) }}{% if optionsArray and optionsArray != '[]'%}, + {{ optionsArray|raw }} +{% else %} + +{% endif %} ); {% endfor %} } @@ -161,7 +154,9 @@ class {{ className }} implements Migration {% if options.autoincrement is defined %}{% set items = items|merge(["'autoincrement' => " ~ dmpr.dumpBoolean(options.autoincrement)]) %}{% endif -%} {% if options.comment is defined %}{% set items = items|merge(["'comment' => " ~ dmpr.dumpString(options.comment)]) %}{% endif -%} {% if columnExtendedOptions is not empty %}{% set items = items|merge(["'oro_options' => " ~ dmpr.dumpOptionsArray(columnExtendedOptions)]) %}{% endif -%} - [{{ items|join(', ')|raw }}] + {% if items|length > 0 %} + [{{ items|join(', ')|raw }}] + {% endif %} {% endspaceless %} {% endmacro %} diff --git a/src/Resources/views/schema-template.php.twig b/src/Resources/views/schema-template.php.twig index 65f39ee..9373235 100644 --- a/src/Resources/views/schema-template.php.twig +++ b/src/Resources/views/schema-template.php.twig @@ -7,10 +7,6 @@ use Doctrine\DBAL\Schema\Schema; use Okvpn\Bundle\MigrationBundle\Migration\Installation; use Okvpn\Bundle\MigrationBundle\Migration\QueryBag; -/** - * @SuppressWarnings(PHPMD.TooManyMethods) - * @SuppressWarnings(PHPMD.ExcessiveClassLength) - */ class {{ className }} implements Installation { /** @@ -51,7 +47,8 @@ class {{ className }} implements Installation $table = $schema->createTable('{{ table.name }}'); {% for column in table.columns %} {% set columnExtendedOptions = extendedOptions[table.name][column.name] is defined ? extendedOptions[table.name][column.name] : null %} - $table->addColumn('{{ column.name }}', '{{ column.type.name }}', {{ dmpr.dumpColumnOptions(column, columnExtendedOptions) }}); + {%- set dumpColumnOptions = dmpr.dumpColumnOptions(column, columnExtendedOptions) %} + $table->addColumn('{{ column.name }}', '{{ column.type.name }}'{{ (dumpColumnOptions ? ', ' ~ dumpColumnOptions : '')|raw }}); {% endfor %} {% for index in table.indexes %} {% if index.isPrimary %} @@ -104,7 +101,9 @@ class {{ className }} implements Installation {% if options.autoincrement is defined %}{% set items = items|merge(["'autoincrement' => " ~ dmpr.dumpBoolean(options.autoincrement)]) %}{% endif -%} {% if options.comment is defined %}{% set items = items|merge(["'comment' => " ~ dmpr.dumpString(options.comment)]) %}{% endif -%} {% if columnExtendedOptions is not empty %}{% set items = items|merge(["'oro_options' => " ~ dmpr.dumpOptionsArray(columnExtendedOptions)]) %}{% endif -%} - [{{ items|join(', ')|raw }}] + {% if items|length > 0 %} + [{{ items|join(', ')|raw }}] + {% endif %} {% endspaceless %} {% endmacro %} diff --git a/src/Tools/SchemaDiffDumper.php b/src/Tools/SchemaDiffDumper.php index f9306ec..09697ec 100644 --- a/src/Tools/SchemaDiffDumper.php +++ b/src/Tools/SchemaDiffDumper.php @@ -3,6 +3,7 @@ namespace Okvpn\Bundle\MigrationBundle\Tools; use Doctrine\DBAL\Schema\SchemaDiff; +use Twig\Environment; class SchemaDiffDumper { @@ -16,7 +17,7 @@ class SchemaDiffDumper protected $schemaDiff; /** - * @var \Twig_Environment + * @var Environment */ protected $twig; @@ -26,10 +27,10 @@ class SchemaDiffDumper protected $migrationPath; /** - * @param \Twig_Environment $twig + * @param Environment $twig * @param string $migrationPath */ - public function __construct(\Twig_Environment $twig, string $migrationPath) + public function __construct($twig, string $migrationPath) { $this->twig = $twig; $this->migrationPath = $migrationPath; @@ -58,6 +59,10 @@ public function dump( $version = self::DEFAULT_VERSION, array $extendedOptions = null ) { + if ($this->twig === null) { + throw new \RuntimeException('Twig is required. You need install "symfony/twig-bundle" to use this command'); + } + $migrationPath = trim(preg_replace('/\//', '\\', $this->migrationPath), "\\"); $content = $this->twig->render( self::SCHEMA_TEMPLATE, diff --git a/src/Tools/SchemaDumper.php b/src/Tools/SchemaDumper.php index 6879fe4..bff9ed8 100644 --- a/src/Tools/SchemaDumper.php +++ b/src/Tools/SchemaDumper.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Visitor\AbstractVisitor; +use Twig\Environment; class SchemaDumper extends AbstractVisitor { @@ -17,7 +18,7 @@ class SchemaDumper extends AbstractVisitor protected $schema; /** - * @var \Twig_Environment + * @var Environment */ protected $twig; @@ -27,10 +28,10 @@ class SchemaDumper extends AbstractVisitor protected $migrationPath; /** - * @param \Twig_Environment $twig + * @param Environment $twig * @param string $migrationPath */ - public function __construct(\Twig_Environment $twig, string $migrationPath) + public function __construct($twig, string $migrationPath) { $this->twig = $twig; $this->migrationPath = $migrationPath; @@ -59,6 +60,10 @@ public function dump( $version = self::DEFAULT_VERSION, array $extendedOptions = null ) { + if ($this->twig === null) { + throw new \RuntimeException('Twig is required. You need install "symfony/twig-bundle" to use this command'); + } + $migrationPath = trim(preg_replace('/\//', '\\', $this->migrationPath), "\\"); $content = $this->twig->render( self::SCHEMA_TEMPLATE,