From 1ae97b3b513c48e863763fb5cf2c5bff41f88292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Wed, 7 May 2014 12:38:52 -0700 Subject: [PATCH] Initial release --- .gitignore | 2 + Command/ExecuteCommand.php | 48 +++++ Command/GenerateCommand.php | 121 +++++++++++++ Command/LatestCommand.php | 48 +++++ Command/MigrateCommand.php | 42 +++++ Command/StatusCommand.php | 42 +++++ Command/VersionCommand.php | 42 +++++ DependencyInjection/Configuration.php | 52 ++++++ .../EzPublishMigrationsExtension.php | 41 +++++ KreaitEzPublishMigrationsBundle.php | 19 ++ LICENSE | 21 +++ Migrations/AbstractMigration.php | 40 +++++ README.md | 169 ++++++++++++++++++ Traits/CommandTrait.php | 67 +++++++ composer.json | 37 ++++ 15 files changed, 791 insertions(+) create mode 100644 .gitignore create mode 100644 Command/ExecuteCommand.php create mode 100644 Command/GenerateCommand.php create mode 100644 Command/LatestCommand.php create mode 100644 Command/MigrateCommand.php create mode 100644 Command/StatusCommand.php create mode 100644 Command/VersionCommand.php create mode 100644 DependencyInjection/Configuration.php create mode 100644 DependencyInjection/EzPublishMigrationsExtension.php create mode 100644 KreaitEzPublishMigrationsBundle.php create mode 100644 LICENSE create mode 100644 Migrations/AbstractMigration.php create mode 100644 README.md create mode 100644 Traits/CommandTrait.php create mode 100644 composer.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c55784d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +composer.lock +/vendor/ diff --git a/Command/ExecuteCommand.php b/Command/ExecuteCommand.php new file mode 100644 index 0000000..e75a5f7 --- /dev/null +++ b/Command/ExecuteCommand.php @@ -0,0 +1,48 @@ +setName('ezpublish:migrations:execute'); + } + + /** + * {@inheritDoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseExecuteCommand::execute($input, $output); + } +} diff --git a/Command/GenerateCommand.php b/Command/GenerateCommand.php new file mode 100644 index 0000000..8eaf2da --- /dev/null +++ b/Command/GenerateCommand.php @@ -0,0 +1,121 @@ +; + +use Kreait\EzPublish\MigrationsBundle\Migrations\AbstractMigration as AbstractEzPublishMigration; +use Doctrine\DBAL\Schema\Schema; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +class Version extends AbstractEzPublishMigration +{ + /** + * Description of this up migration + * + * @param Schema $schema + */ + public function up(Schema $schema) + { + // this up() migration is auto-generated, please modify it to your needs + + } + + /** + * Description of this down migration + * + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + + } +} +'; + /** + * {@inheritDoc} + */ + protected function configure() + { + BaseGenerateCommand::configure(); + + $this->setName('ezpublish:migrations:generate'); + } + + /** + * {@inheritDoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseGenerateCommand::execute($input, $output); + } + + /** + * {@inheritDoc} + */ + protected function generateMigration(Configuration $configuration, InputInterface $input, $version, $up = null, $down = null) + { + $placeHolders = array( + '', + '', + '', + '' + ); + $replacements = array( + $configuration->getMigrationsNamespace(), + $version, + $up ? " " . implode("\n ", explode("\n", $up)) : null, + $down ? " " . implode("\n ", explode("\n", $down)) : null + ); + $code = str_replace($placeHolders, $replacements, $this->template); + $dir = $configuration->getMigrationsDirectory(); + $dir = $dir ? $dir : getcwd(); + $dir = rtrim($dir, '/'); + $path = $dir . '/Version' . $version . '.php'; + + if ( ! file_exists($dir)) { + throw new \InvalidArgumentException(sprintf('Migrations directory "%s" does not exist.', $dir)); + } + + file_put_contents($path, $code); + + if ($editorCmd = $input->getOption('editor-cmd')) { + shell_exec($editorCmd . ' ' . escapeshellarg($path)); + } + + return $path; + } +} diff --git a/Command/LatestCommand.php b/Command/LatestCommand.php new file mode 100644 index 0000000..0356c5e --- /dev/null +++ b/Command/LatestCommand.php @@ -0,0 +1,48 @@ +setName('ezpublish:migrations:latest'); + } + + /** + * {@inheritDoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseLatestCommand::execute($input, $output); + } +} diff --git a/Command/MigrateCommand.php b/Command/MigrateCommand.php new file mode 100644 index 0000000..56459ec --- /dev/null +++ b/Command/MigrateCommand.php @@ -0,0 +1,42 @@ +setName('ezpublish:migrations:migrate'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseMigrateCommand::execute($input, $output); + } +} diff --git a/Command/StatusCommand.php b/Command/StatusCommand.php new file mode 100644 index 0000000..f20e92a --- /dev/null +++ b/Command/StatusCommand.php @@ -0,0 +1,42 @@ +setName('ezpublish:migrations:status'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseStatusCommand::execute($input, $output); + } +} diff --git a/Command/VersionCommand.php b/Command/VersionCommand.php new file mode 100644 index 0000000..27c5325 --- /dev/null +++ b/Command/VersionCommand.php @@ -0,0 +1,42 @@ +setName('ezpublish:migrations:version'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + /** @var ContainerInterface $container */ + $container = $this->getApplication()->getKernel()->getContainer(); + + $this->setMigrationConfiguration($this->getBasicConfiguration($container, $output)); + + $configuration = $this->getMigrationConfiguration($input, $output); + $this->configureMigrations($container, $configuration); + + BaseVersionCommand::execute($input, $output); + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..8598f9a --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,52 @@ +rootIdentifier = $rootIdentifier; + } + + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root($this->rootIdentifier); + + $rootNode + ->children() + ->scalarNode('dir_name')->defaultValue('%kernel.root_dir%/EzPublishMigrations')->cannotBeEmpty()->end() + ->scalarNode('namespace')->defaultValue('Application\Migrations')->cannotBeEmpty()->end() + ->scalarNode('table_name')->defaultValue('ezmigration_versions')->cannotBeEmpty()->end() + ->scalarNode('name')->defaultValue('Application Migrations')->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/DependencyInjection/EzPublishMigrationsExtension.php b/DependencyInjection/EzPublishMigrationsExtension.php new file mode 100644 index 0000000..377295a --- /dev/null +++ b/DependencyInjection/EzPublishMigrationsExtension.php @@ -0,0 +1,41 @@ +getAlias()); + $config = $this->processConfiguration($configuration, $configs); + + foreach ($config as $key => $value) { + $container->setParameter($this->getAlias().'.'.$key, $value); + } + } + + /** + * {@inheritDoc} + */ + public function getAlias() + { + return 'ezpublish_migrations'; + } +} diff --git a/KreaitEzPublishMigrationsBundle.php b/KreaitEzPublishMigrationsBundle.php new file mode 100644 index 0000000..512d4dd --- /dev/null +++ b/KreaitEzPublishMigrationsBundle.php @@ -0,0 +1,19 @@ +container = $container; + } + + /** + * Returns the container + * + * @return ContainerInterface + */ + public function getContainer() + { + return $this->container; + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..93df715 --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +# eZ Publish 5 Migrations + +[![Latest Stable Version](https://poser.pugx.org/kreait/ezpublish-migrations-bundle/v/stable.png)](https://packagist.org/packages/kreait/ezpublish-migrations-bundle) +[![Latest Unstable Version](https://poser.pugx.org/kreait/ezpublish-migrations-bundle/v/unstable.png)](https://packagist.org/packages/kreait/ezpublish-migrations-bundle) +[![License](https://poser.pugx.org/kreait/ezpublish-migrations-bundle/license.png)](https://packagist.org/packages/kreait/ezpublish-migrations-bundle) + +Migrations for eZ Publish 5, almost identical to Symfony's +[DoctrineMigrationsBundle](https://github.com/doctrine/DoctrineMigrationsBundle). + + + +## Installation + +Follow these steps to install the bundle in your eZ Publish 5 project. + +Add the following to your composer.json file: + +``` +{ + "require": { + "doctrine/migrations": "dev-master", + "kreait/ezpublish-migrations-bundle": "~1" + } +} +``` + +Update the vendor libraries: + +```bash +$ php composer.phar update +``` + +If everything worked, the EzPublishMigrationsBundle can now be found at vendor/kreait/ezpublish-migrations-bundle. + +Finally, be sure to enable the bundle in EzPublishKernel.php by including the following: + +```php +// ezpublish/EzPublishKernel.php +public function registerBundles() +{ + $bundles = array( + //... + new Kreait\EzPublish\MigrationsBundle\KreaitEzPublishMigrationsBundle(), + ); +} +``` + +## Configuration + +You can configure the path, namespace, table_name and name in your config.yml. +The examples below are the default values. + +``` +// ezpublish/config/config.yml +ezpublish_migrations: + dir_name: %kernel.root_dir%/EzPublishMigrations + namespace: Application\Migrations + table_name: ezmigration_versions + name: Application Migrations +``` + +## Usage + +All of the migrations functionality is contained in the following commands: + +``` +ezpublish:migrations + :execute Execute a single migration version up or down manually. + :generate Generate a blank migration class. + :migrate Execute a migration to a specified version or the latest available version. + :status View the status of a set of migrations. + :version Manually add and delete migration versions from the version table. +``` + +The usage is identical to Symfony's DoctrineMigrationBundle, except for the missing `:diff` command. +Please have a look at the +[official documention](http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html) +for further information. + +## Examples + +### Enable users with the 'Anonymous' role to access the siteacess 'mysiteaccess' without having to log in + +```bash +$ ezpublish/console ezpublish:migrations:generate +Generated new migration class to "/var/www/ezpublish/EzPublishMigrations/Version20140508021924.php" +``` + +```php +// ezpublish/EzPublishMigrations/Version20140508021924.php +namespace Application\Migrations; + +use ... + +class Version20140508021924 extends AbstractEzPublishMigration +{ + /** + * @var string + */ + private $siteAccessIdentifier = 'mysiteaccess'; + + /** + * Removes all existing siteaccess limitations and adds a new one for the role 'Anonymous' + * + * @param Schema $schema + */ + public function up(Schema $schema) + { + /** @var $repository \eZ\Publish\API\Repository\Repository */ + $repository = $this->getContainer()->get('ezpublish.api.repository'); + $userService = $repository->getUserService(); + $administratorUser = $userService->loadUser( 14 ); + $repository->setCurrentUser( $administratorUser ); + + $roleService = $repository->getRoleService(); + + $role = $roleService->loadRole(1); // Anonymous + + $limitation = new SiteAccessLimitation(); + $limitation->limitationValues[] = sprintf('%u', crc32($this->siteAccessIdentifier)); + + $policy = $roleService->newPolicyCreateStruct('user', 'login'); + $policy->addLimitation($limitation); + + $roleService->addPolicy($role, $policy); + + $message = sprintf("SELECT 'Added SiteAccess limitation for \"%s\" to role \"%s\"'", $this->siteAccessIdentifier, $role->identifier); + $this->addSql($message); + } + + /** + * Removes access to the new siteaccess for the role 'Anonymous' + * + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->addSql("SELECT 'We should probably remove access to {$this->siteAccessIdentifier} here'"); + } +} + +``` + +## Caveats + +### No "real" SQL statements + +When you create a migration using only eZ Publish's API methods, no actual SQL statements are executed. This results in the following message: + +``` +Migration was executed but did not result in any SQL statements. +``` + +You can avoid this message by adding a dummy SQL statement at the end of your `up()` and `down()` method: + +```php +public function up(Schema $schema) +{ + // ... + $this->addSql("SELECT 'Description of what we did here'"); +} +``` + + +## Acknowledgments + +- [Doctrine Project](http://www.doctrine-project.org/) for providing the underlying migration functionality +- [Symfony](http://symfony.com/) for being the blueprint for this bundle +- [Magic Internet GmbH](http://www.magicinternet.de/), especially [@m-keil](https://github.com/m-keil) for the initial methodical blueprint diff --git a/Traits/CommandTrait.php b/Traits/CommandTrait.php new file mode 100644 index 0000000..8d50edb --- /dev/null +++ b/Traits/CommandTrait.php @@ -0,0 +1,67 @@ +getParameter('ezpublish_migrations.dir_name'); + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + } + + $configuration->setMigrationsNamespace($container->getParameter('ezpublish_migrations.namespace')); + $configuration->setMigrationsDirectory($dir); + $configuration->registerMigrationsFromDirectory($dir); + $configuration->setName($container->getParameter('ezpublish_migrations.name')); + $configuration->setMigrationsTableName($container->getParameter('ezpublish_migrations.table_name')); + + self::injectContainerToMigrations($container, $configuration->getMigrations()); + } + + /** + * Injects the container to migrations aware of it + * + * @param ContainerInterface $container + * @param Version[] $versions + */ + protected function injectContainerToMigrations(ContainerInterface $container, array $versions) + { + foreach ($versions as $version) { + $migration = $version->getMigration(); + if ($migration instanceof ContainerAwareInterface) { + $migration->setContainer($container); + } + } + } + + + /** + * Generates a doctrine configuration object with eZ Publish's database connection + * + * @param ContainerInterface $container + * @param OutputInterface $output + * @return \Doctrine\DBAL\Migrations\Configuration\Configuration + */ + protected function getBasicConfiguration(ContainerInterface $container, OutputInterface $output) + { + $outputWriter = new OutputWriter(function($message) use ($output) { + return $output->writeln($message); + }); + + return new Configuration($container->get( 'ezpublish.connection' )->getConnection(), $outputWriter); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e305cf1 --- /dev/null +++ b/composer.json @@ -0,0 +1,37 @@ +{ + "name": "kreait/ezpublish-migrations-bundle", + "description": "Migrations for eZ Publish 5", + "keywords": ["Migrations", "eZ Publish"], + "homepage": "https://github.com/kreait/ezpublish-migrations-bundle", + "license": "MIT", + "authors": [ + { + "name": "Jérôme Gamez", + "homepage": "https://github.com/jeromegamez", + "email": "jerome@kreait.com" + }, + { + "name": "The GitHub Community", + "homepage": "https://github.com/kreait/EzPublishMigrationsBundle/graphs/contributors" + } + ], + "require": { + "php": ">=5.4", + "doctrine/migrations": "~1.0@dev" + }, + "require-dev": { + "symfony/config": "~2", + "symfony/console": "~2", + "symfony/dependency-injection": "~2", + "symfony/http-kernel": "~2", + "symfony/yaml": "~2" + }, + "autoload": { + "psr-4": { "Kreait\\EzPublish\\MigrationsBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +}