diff --git a/Module.php b/Module.php index 82315e7..5bd95fa 100644 --- a/Module.php +++ b/Module.php @@ -9,24 +9,24 @@ namespace ZendDbMigrations; +use Zend\Db\ResultSet\ResultSet; +use Zend\Db\TableGateway\TableGateway; use Zend\Mvc\ModuleRouteListener; +use Zend\Mvc\MvcEvent; use Zend\ModuleManager\Feature\AutoloaderProviderInterface; use Zend\ModuleManager\Feature\ConfigProviderInterface; use Zend\ModuleManager\Feature\ConsoleUsageProviderInterface; use Zend\Console\Adapter\AdapterInterface as Console; -use Zend\ModuleManager\Feature\ConsoleBannerProviderInterface; class Module implements AutoloaderProviderInterface, ConfigProviderInterface, - ConsoleUsageProviderInterface, - ConsoleBannerProviderInterface + ConsoleUsageProviderInterface { - - public function onBootstrap($e) + public function onBootstrap(MvcEvent $e) { $e->getApplication()->getServiceManager()->get('translator'); - $eventManager = $e->getApplication()->getEventManager(); + $eventManager = $e->getApplication()->getEventManager(); $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($eventManager); } @@ -46,17 +46,45 @@ public function getAutoloaderConfig() ), ); } - - public function getConsoleBanner(Console $console){ - return 'DB Migrations Module'; + + public function getServiceConfig() + { + return array( + 'factories' => array( + 'ZendDbMigrations\Model\MigrationVersionTable' => function ($sm) { + /** @var $sm */ + $tableGateway = $sm->get('MigrationVersionTableGateway'); + $table = new Model\MigrationVersionTable($tableGateway); + return $table; + }, + 'MigrationVersionTableGateway' => function ($sm) { + $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter'); + $resultSetPrototype = new ResultSet(); + $resultSetPrototype->setArrayObjectPrototype(new Model\MigrationVersion()); + return new TableGateway(Library\Migration::MIGRATION_TABLE, $dbAdapter, null, $resultSetPrototype); + }, + ), + ); } - public function getConsoleUsage(Console $console){ - //description command + public function getConsoleUsage(Console $console) + { return array( - 'db_migrations_version' => 'Get current migration version', - 'db_migrations_migrate []' => 'Execute migrate', - 'db_migrations_generate' => 'Generate new migration class' + 'Migrations', + + 'migration version' => 'Get current migration version', + + 'migration list [--all]' => 'List available migrations', + array('--all', 'Include applied migrations'), + + 'migration migrate []' => 'Execute migrate', + array( + '--force', + 'Force apply migration even if it\'s older than the last migrated. Works only with explicitly set.' + ), + array('--down', 'Force apply down migration. Works only with --force flag set.'), + + 'migration generate' => 'Generate new migration class' ); } } diff --git a/README.md b/README.md index b175112..5d8eea0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ ZendDbMigrations ============ +## Try new module based on this fork [ZfSimpleMigrations](https://github.com/vgarvardt/ZfSimpleMigrations). +## Fork is abandoned due to original module author not responding to issues and requests. + Установка ------------- Добавьте в composer.json проекта в секцию require @@ -18,9 +21,10 @@ php composer.phar update Список добавляемых консольных комманд ``` bash -db_migrations_version - возвращает номер текущей версии -db_migrations_migrate [] - выполнить или откатить миграцию, номер версии необязательный параметр -db_migrations_generate - Сгенерировать каркас класса миграции +migration version - возвращает номер текущей версии +migration list [--all] - выводит список доступных миграций +migration migrate [] [--force] [--down] - выполнить или откатить миграцию, номер версии необязательный параметр +migration generate - Сгенерировать каркас класса миграции ``` Все миграции по умолчанию будут хранится в каталоге @@ -39,22 +43,26 @@ namespace ZendDbMigrations\Migrations; use ZendDbMigrations\Library\AbstractMigration; use Zend\Db\Metadata\MetadataInterface; -class Version20121112230913 extends AbstractMigration { - - public function up(MetadataInterface $schema){ +class Version20121112230913 extends AbstractMigration +{ + public static $description = "Migration description"; + + public function up(MetadataInterface $schema) + { //$this->addSql(/*Sql instruction*/); } - - public function down(MetadataInterface $schema){ + + public function down(MetadataInterface $schema) + { //$this->addSql(/*Sql instruction*/); } } ``` выполнить миграцию можно двумя способами -запустив команду db_migrations_migrate без параметров -или с указанием версии -db_migrations_migrate 20121112230913 -Version20121112230913 - здесь 20121112230913 будет версией миграции +* запустив команду migration migrate без параметров +* или с указанием версии + * `migration migrate 20121112230913` + * `Version20121112230913` - здесь `20121112230913` будет версией миграции -http://vadim-knyzev.blogspot.com/ \ No newline at end of file +http://vadim-knyzev.blogspot.com/ diff --git a/composer.json b/composer.json index 339a2be..e497d8d 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,11 @@ "name": "Vadim Knyzev", "email": "vadim.knyzev@gmail.com", "homepage": "http://vadim-knyzev.blogspot.com/" + }, + { + "name": "Vladimir Garvardt", + "email": "vgarvardt@gmail.com", + "homepage": "http://www.linkedin.com/in/vgarvardt/" } ], "require": { diff --git a/config/module.config.php b/config/module.config.php index 4f4ab5b..3873eb1 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -9,33 +9,43 @@ 'console' => array( 'router' => array( 'routes' => array( - 'db_migrations_version' => array( - 'type' => 'simple', + 'migration-version' => array( + 'type' => 'simple', 'options' => array( - 'route' => 'db_migrations_version [--env=]', + 'route' => 'migration version [--env=]', 'defaults' => array( 'controller' => 'ZendDbMigrations\Controller\Migrate', - 'action' => 'version' + 'action' => 'version' ) ) ), - 'db_migrations_migrate' => array( - 'type' => 'simple', + 'migration-list' => array( + 'type' => 'simple', 'options' => array( - 'route' => 'db_migrations_migrate [] [--env=]', + 'route' => 'migration list [--env=] [--all]', 'defaults' => array( 'controller' => 'ZendDbMigrations\Controller\Migrate', - 'action' => 'migrate' + 'action' => 'list' ) ) ), - 'db_migrations_generate' => array( - 'type' => 'simple', + 'migration-migrate' => array( + 'type' => 'simple', 'options' => array( - 'route' => 'db_migrations_generate [--env=]', + 'route' => 'migration migrate [] [--env=] [--force] [--down]', 'defaults' => array( 'controller' => 'ZendDbMigrations\Controller\Migrate', - 'action' => 'generateMigrationClass' + 'action' => 'migrate' + ) + ) + ), + 'migration-generate' => array( + 'type' => 'simple', + 'options' => array( + 'route' => 'migration generate [--env=]', + 'defaults' => array( + 'controller' => 'ZendDbMigrations\Controller\Migrate', + 'action' => 'generateMigrationClass' ) ) ) diff --git a/src/ZendDbMigrations/Controller/MigrateController.php b/src/ZendDbMigrations/Controller/MigrateController.php index 85069b5..fabb525 100644 --- a/src/ZendDbMigrations/Controller/MigrateController.php +++ b/src/ZendDbMigrations/Controller/MigrateController.php @@ -10,7 +10,7 @@ namespace ZendDbMigrations\Controller; use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; +use Zend\Mvc\MvcEvent; use Zend\Console\Request as ConsoleRequest; use ZendDbMigrations\Library\Migration; use ZendDbMigrations\Library\MigrationException; @@ -23,67 +23,97 @@ class MigrateController extends AbstractActionController { /** - * Создать объект класса миграций - * @return \Migrations\Library\Migration + * @var \ZendDbMigrations\Library\Migration */ - protected function getMigration(){ + protected $migration; + /** + * @var \ZendDbMigrations\Model\MigrationVersionTable + */ + protected $migrationVersionTable; + /** + * @var OutputWriter + */ + protected $output; + + public function onDispatch(MvcEvent $e) + { + if (!$this->getRequest() instanceof ConsoleRequest) { + throw new \RuntimeException('You can only use this action from a console!'); + } + + /** @var $adapter \Zend\Db\Adapter\Adapter */ $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); $config = $this->getServiceLocator()->get('Configuration'); - + $console = $this->getServiceLocator()->get('console'); - - $output = null; - - if($config['migrations']['show_log']) - { - $output = new OutputWriter(function($message) use($console) { - $console->write($message . "\n"); - }); + + if ($config['migrations']['show_log']) { + $this->output = new OutputWriter(function ($message) use ($console) { + $console->write($message . "\n"); + }); } - - return new Migration($adapter, $config['migrations']['dir'], $config['migrations']['namespace'], $output); + + $this->migrationVersionTable = $this->getServiceLocator()->get('ZendDbMigrations\Model\MigrationVersionTable'); + $this->migration = new Migration($adapter, $config['migrations'], $this->migrationVersionTable, $this->output); + + return parent::onDispatch($e); } - + /** * Получить текущую версию миграции * @return integer */ - public function versionAction(){ - $migration = $this->getMigration(); - - return sprintf("Current version %s\n", $migration->getCurrentVersion()); + public function versionAction() + { + return sprintf("Current version %s\n", $this->migrationVersionTable->getCurrentVersion()); + } + + public function listAction() + { + $migrations = $this->migration->getMigrationClasses($this->getRequest()->getParam('all')); + $list = array(); + foreach ($migrations as $m) { + $list[] = sprintf("%s %s - %s", $m['applied'] ? '-' : '+', $m['version'], $m['description']); + } + return (empty($list) ? 'No migrations to execute.' : implode("\n", $list)) . "\n"; } - + /** * Мигрировать */ - public function migrateAction(){ - $migration = $this->getMigration(); - + public function migrateAction() + { $version = $this->getRequest()->getParam('version'); - - if(is_null($version) && $migration->getCurrentVersion() >= $migration->getMaxMigrationNumber($migration->getMigrationClasses())) + + $migrations = $this->migration->getMigrationClasses(); + $currentMigrationVersion = $this->migrationVersionTable->getCurrentVersion(); + $force = $this->getRequest()->getParam('force'); + + if (is_null($version) && $force) { + return "Can't force migrate without migration version explicitly set."; + } + if (!$force && is_null($version) && $currentMigrationVersion >= $this->migration->getMaxMigrationNumber($migrations)) { return "No migrations to execute.\n"; - - try{ - $migration->migrate($version); - return "Migrations executed!\n"; } - catch (MigrationException $e) { - return "ZendDbMigrations\Library\MigrationException\n" . $e->getMessage() . "\n"; + + try { + $this->migration->migrate($version, $force, $this->getRequest()->getParam('down')); + return "Migrations executed!\n"; + } catch (MigrationException $e) { + return "ZendDbMigrations\\Library\\MigrationException\n" . $e->getMessage() . "\n"; } } - + /** * Сгенерировать каркасный класс для новой миграции */ - public function generateMigrationClassAction(){ - $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); + public function generateMigrationClassAction() + { $config = $this->getServiceLocator()->get('Configuration'); - + $generator = new GeneratorMigrationClass($config['migrations']['dir'], $config['migrations']['namespace']); - $className = $generator->generate(); - - return sprintf("Generated class %s\n", $className); + $classPath = $generator->generate(); + + return sprintf("Generated class %s\n", realpath($classPath)); } -} \ No newline at end of file +} diff --git a/src/ZendDbMigrations/Library/AbstractMigration.php b/src/ZendDbMigrations/Library/AbstractMigration.php index 17c9976..abb4f6f 100644 --- a/src/ZendDbMigrations/Library/AbstractMigration.php +++ b/src/ZendDbMigrations/Library/AbstractMigration.php @@ -45,18 +45,6 @@ public function getDownSql(){ return $this->sql; } - - /** - * Выполнить миграцию - * @param MetadataInterface $schema - */ - abstract public function up(MetadataInterface $schema); - - /** - * Откатить миграцию - * @param MetadataInterface $schema - */ - abstract public function down(MetadataInterface $schema); } ?> diff --git a/src/ZendDbMigrations/Library/GeneratorMigrationClass.php b/src/ZendDbMigrations/Library/GeneratorMigrationClass.php index d74ee80..588bf27 100644 --- a/src/ZendDbMigrations/Library/GeneratorMigrationClass.php +++ b/src/ZendDbMigrations/Library/GeneratorMigrationClass.php @@ -5,25 +5,25 @@ /** * Генерирование каркаса миграции */ -class GeneratorMigrationClass { - +class GeneratorMigrationClass +{ protected $migrationsFolder; protected $migrationNamespace; /** - * Конструктор - * @param type $folderMigrationClasses каталог в который сохранить сгенерированную миграцию - * @param string $namespace Нэймспэйс указанный в классе миграции + * @param string $folderMigrationClasses каталог в который сохранить сгенерированную миграцию + * @param string $migrationNamespace указанный в классе миграции + * @throws \Exception */ - public function __construct($folderMigrationClasses, $migrationNamespace) { + public function __construct($folderMigrationClasses, $migrationNamespace) + { $this->migrationsFolder = $folderMigrationClasses; $this->migrationNamespace = $migrationNamespace; - - - if(!file_exists($this->migrationsFolder)) - if(!mkdir ($this->migrationsFolder, 0775)) - throw new \Exception(sprintf('Not permitted to created directory %s', - $this->migrationsFolder)); + + if (!file_exists($this->migrationsFolder)) + if (!mkdir($this->migrationsFolder, 0775)) + throw new \Exception(sprintf('Not permitted to created directory %s', + $this->migrationsFolder)); } /** @@ -31,29 +31,31 @@ public function __construct($folderMigrationClasses, $migrationNamespace) { * @return string Путь к классу * @throws \Exception */ - public function generate(){ - + public function generate() + { + $className = sprintf('Version%s', date('YmdHis', time())); - $classPath = $this->migrationsFolder . '/' . $className .'.php'; - - if(!is_writable($this->migrationsFolder)) + $classPath = $this->migrationsFolder . '/' . $className . '.php'; + + if (!is_writable($this->migrationsFolder)) throw new \Exception(sprintf('%s path is not writable!', $classPath)); - - if(file_exists($classPath)) + + if (file_exists($classPath)) throw new \Exception(sprintf('Migration %s if exists!', $className)); - + file_put_contents($classPath, $this->getTemplate($className, $this->migrationNamespace)); - + return $classPath; } - + /** * Шаблон для класса миграции - * @param type $className - * @param type $namespace + * @param string $className + * @param string $namespace * @return string */ - protected function getTemplate($className, $namespace){ + protected function getTemplate($className, $namespace) + { return sprintf('addSql(/*Sql instruction*/); } - public function down(MetadataInterface $schema){ + public function down(MetadataInterface $schema) + { //$this->addSql(/*Sql instruction*/); } -}', $namespace, $className); +} +', $namespace, $className); } } diff --git a/src/ZendDbMigrations/Library/Migration.php b/src/ZendDbMigrations/Library/Migration.php index 2cdf293..f8215b2 100644 --- a/src/ZendDbMigrations/Library/Migration.php +++ b/src/ZendDbMigrations/Library/Migration.php @@ -3,292 +3,270 @@ namespace ZendDbMigrations\Library; use Zend\Db\Adapter\Adapter; -use Zend\Db\Sql\Sql; use Zend\Db\Adapter\Driver\Pdo\Pdo; +use Zend\Db\Adapter\Exception\InvalidQueryException; use Zend\Db\Metadata\Metadata; -use ZendDbMigrations\Library\MigrationInterface; use ZendDbMigrations\Library\OutputWriter; +use ZendDbMigrations\Model\MigrationVersionTable; /** * Основная логика работы с миграциями */ -class Migration { - - protected $migrationTable = 'migration_version'; +class Migration +{ + const MIGRATION_TABLE = 'migration_version'; + protected $migrationClassFolder; protected $namespaceMigrationsClasses; protected $adapter; + /** + * @var \Zend\Db\Adapter\Driver\ConnectionInterface + */ protected $connection; protected $metadata; + protected $migrationVersionTable; protected $outputWriter; /** - * Конструктор * @param \Zend\Db\Adapter\Adapter $adapter - * @param string $migrationClassFolder каталог с классами миграций + * @param array $config + * @param \ZendDbMigrations\Model\MigrationVersionTable $migrationVersionTable + * @param OutputWriter $writer + * @throws \Exception */ - public function __construct(Adapter $adapter, $migrationClassFolder, $namespaceMigrationsClasses, OutputWriter $writer = null) { + public function __construct(Adapter $adapter, array $config, MigrationVersionTable $migrationVersionTable, OutputWriter $writer = null) + { $this->adapter = $adapter; $this->metadata = new Metadata($this->adapter); $this->connection = $this->adapter->getDriver()->getConnection(); - $this->migrationClassFolder = $migrationClassFolder; - $this->namespaceMigrationsClasses = $namespaceMigrationsClasses; + $this->migrationClassFolder = $config['dir']; + $this->namespaceMigrationsClasses = $config['namespace']; + $this->migrationVersionTable = $migrationVersionTable; $this->outputWriter = is_null($writer) ? new OutputWriter() : $writer; - - if(is_null($migrationClassFolder)) + + if (is_null($this->migrationClassFolder)) throw new \Exception('Unknown directory!'); - - if(is_null($namespaceMigrationsClasses)) + + if (is_null($this->namespaceMigrationsClasses)) throw new \Exception('Unknown namespaces!'); - - if(!file_exists($this->migrationClassFolder)) - if(!mkdir ($this->migrationClassFolder, 0775)) - throw new \Exception(sprintf('Not permitted to created directory %s', - $this->migrationClassFolder)); - - + + if (!file_exists($this->migrationClassFolder)) + if (!mkdir($this->migrationClassFolder, 0775)) + throw new \Exception(sprintf('Not permitted to created directory %s', + $this->migrationClassFolder)); + + $this->checkCreateMigrationTable(); } - + /** * Создать таблицу миграций */ - protected function createMigrationTable(){ - $sql = sprintf('CREATE TABLE IF NOT EXISTS "%s" ("id" SERIAL NOT NULL, - "version" bigint NOT NULL, - PRIMARY KEY ("id"));', $this->migrationTable); - $this->connection->execute($sql); - } - - /** - * Получить текущий номер версии - * @return integer Номер текущей миграции - */ - public function getCurrentVersion(){ - $this->createMigrationTable(); - $result = $this->connection->execute(sprintf('SELECT version FROM %s ORDER BY version DESC LIMIT 1', - $this->migrationTable)); - - if($result->count() == 0) - return 0; - - $version = $result->current(); - - return $version['version']; - } - - /** - * Добавить сведения о выполненной миграции - * @param integer $version - */ - protected function markMigrated($version) + protected function checkCreateMigrationTable() { - $this->createMigrationTable(); - $this->connection->execute(sprintf("INSERT INTO %s (version) VALUES (%s)", - $this->migrationTable, - $version - )); + if (strpos($this->connection->getDriverName(), 'mysql') !== false) { + $sql = <<connection->execute(sprintf($sql, Migration::MIGRATION_TABLE)); } /** - * Удалить сведения о выполенной миграции - * @param string $version + * @param int $version Номер версии к которой нужно мигрировать, если не указано то будут выполнены все новые миграции + * @param bool $force Применять указанную миграцию без лишних вопросов + * @param bool $down Применить откат миграции указанной версии + * @throws MigrationException */ - protected function markNotMigrated($version) + public function migrate($version = null, $force = false, $down = false) { - $this->createMigrationTable(); - $this->connection->execute(sprintf("DELETE FROM %s WHERE version=%s", - $this->migrationTable, - $version - )); - } - - /** - * Проверка выполнена ли миграция - * @param integer version - * @return boolean - */ - public function checkExecuteMigration($version){ - $this->createMigrationTable(); - $result = $this->connection->execute(sprintf('SELECT version FROM %s WHERE version=%s', - $this->migrationTable, $version)); - - return $result->count() > 0; - } + $migrations = $this->getMigrationClasses($force); - /** - * Мигрировать - * @param integer $version Номер версии к которой нужно мигрировать, - * если не указано то будут выполнены все новые миграции - * @throws MigrationException - */ - public function migrate($version = null){ - $migrations = $this->getMigrationClasses(); - - if(!is_null($version) && !$this->hasMigrationVersion($migrations, $version)){ + if (!is_null($version) && !$this->hasMigrationVersion($migrations, $version)) { throw new MigrationException(sprintf('Migration version %s is not found!', $version)); } - - - $currentMigrationVersion = $this->getCurrentVersion(); - - if(!is_null($version) && $version == $currentMigrationVersion) + + $currentMigrationVersion = $this->migrationVersionTable->getCurrentVersion(); + if (!is_null($version) && $version == $currentMigrationVersion && !$force) { throw new MigrationException(sprintf('Migration version %s is current version!', $version)); - - + } + $this->connection->beginTransaction(); try { - - //номер миграции не указан либо указанный номер больше последней выполненной миграции -> миграция добавления - if(is_null($version) || (!is_null($version) && $version > $currentMigrationVersion)) - { - foreach($migrations as $migration) - { - $migrationObject = new $migration['class']($this->metadata); - - if($migration['version'] > $currentMigrationVersion) - { - if(is_null($version) || (!is_null($version) && $version >= $migration['version'])) - { - $this->outputWriter->write(sprintf("Execute migration class %s up", $migration['class'])); - - foreach($migrationObject->getUpSql() as $sql){ - $this->connection->execute($sql); - $this->outputWriter->write("Execute sql code \n\n" . $sql . "\n"); - } - - $this->markMigrated($migration['version']); + if ($version && $force) { + foreach ($migrations as $migration) { + if ($migration['version'] == $version) { + // if existing migration is forced to apply - delete it's information from migrated + // to avoid duplicate key error + if (!$down) $this->migrationVersionTable->delete($migration['version']); + $this->applyMigration($migration, $down); + break; + } + } + //номер миграции не указан либо указанный номер больше последней выполненной миграции -> миграция добавления + } elseif (is_null($version) || (!is_null($version) && $version > $currentMigrationVersion)) { + foreach ($migrations as $migration) { + if ($migration['version'] > $currentMigrationVersion) { + if (is_null($version) || (!is_null($version) && $version >= $migration['version'])) { + $this->applyMigration($migration); } } } - } - //номер миграции указан и версия ниже текущей -> откат миграции - else if(!is_null($version) && $version < $currentMigrationVersion) - { + //номер миграции указан и версия ниже текущей -> откат миграции + } elseif (!is_null($version) && $version < $currentMigrationVersion) { $migrationsByDesc = $this->sortMigrationByVersionDesc($migrations); - foreach($migrationsByDesc as $migration) - { - $migrationObject = new $migration['class']($this->metadata); - - if($migration['version'] > $version && $migration['version'] <= $currentMigrationVersion) - { - $this->outputWriter->write(sprintf("Execute migration class %s down", $migration['class'])); - - foreach($migrationObject->getDownSql() as $sql){ - $this->connection->execute($sql); - $this->outputWriter->write("Execute sql code \n\n" . $sql . "\n"); - } - - $this->markNotMigrated($migration['version']); + foreach ($migrationsByDesc as $migration) { + if ($migration['version'] > $version && $migration['version'] <= $currentMigrationVersion) { + $this->applyMigration($migration, true); } - } } - + $this->connection->commit(); - } - catch(\Exception $e){ + } catch (InvalidQueryException $e) { + $this->connection->rollback(); + $msg = sprintf('%s: "%s"; File: %s; Line #%d', $e->getMessage(), $e->getPrevious()->getMessage(), $e->getFile(), $e->getLine()); + throw new MigrationException($msg); + } catch (\Exception $e) { $this->connection->rollback(); - throw new MigrationException($e->getMessage() . '; Line error %' . $e->getLine()); + $msg = sprintf('%s; File: %s; Line #%d', $e->getMessage(), $e->getFile(), $e->getLine()); + throw new MigrationException($msg); } - } - + /** * Отсортировать миграции по версии в обратном порядке * @param \ArrayIterator $migrations * @return \ArrayIterator */ - public function sortMigrationByVersionDesc(\ArrayIterator $migrations){ + public function sortMigrationByVersionDesc(\ArrayIterator $migrations) + { $sortedMigrations = clone $migrations; - - $sortedMigrations->uasort(function($a, $b){ + + $sortedMigrations->uasort(function ($a, $b) { if ($a['version'] == $b['version']) { return 0; } - + return ($a['version'] > $b['version']) ? -1 : 1; }); - + return $sortedMigrations; } - + /** * Проверить существование класса для номера миграции * @param \ArrayIterator $migrations * @param integer $version * @return boolean */ - public function hasMigrationVersion(\ArrayIterator $migrations, $version){ - $classes = new \ArrayIterator(array()); - - $versions = array(); - foreach($migrations as $migration){ - $versions[] = $migration['version']; + public function hasMigrationVersion(\ArrayIterator $migrations, $version) + { + foreach ($migrations as $migration) { + if ($migration['version'] == $version) return true; } - - return in_array($version, $versions) || $version == 0; + + return false; } - + /** * Получить номер максимальной версии миграции * @param \ArrayIterator $migrations * @return integer */ - public function getMaxMigrationNumber(\ArrayIterator $migrations){ - $classes = new \ArrayIterator(array()); - + public function getMaxMigrationNumber(\ArrayIterator $migrations) + { $versions = array(); - foreach($migrations as $migration){ + foreach ($migrations as $migration) { $versions[] = $migration['version']; } - + sort($versions, SORT_NUMERIC); $versions = array_reverse($versions); - + return count($versions) > 0 ? $versions[0] : 0; } /** * Найти список классов миграций - * @return \ArrayIterator [version,class] + * + * @param bool $all + * @return \ArrayIterator */ - public function getMigrationClasses(){ - + public function getMigrationClasses($all = false) + { $classes = new \ArrayIterator(); - + $iterator = new \GlobIterator(sprintf('%s/Version*.php', $this->migrationClassFolder), \FilesystemIterator::KEY_AS_FILENAME); foreach ($iterator as $item) { - if(preg_match('/(Version(\d+))\.php/', $item->getFilename(), $matches)){ - - $className = $this->namespaceMigrationsClasses . '\\' . $matches[1]; - - if(!class_exists($className)) - require_once $this->migrationClassFolder . '/' . $item->getFilename(); - - if(class_exists($className)) - { - $reflection = new \ReflectionClass($className); - if($reflection->implementsInterface('ZendDbMigrations\Library\MigrationInterface')) - { - $classes->append(array( - 'version' => $matches[2], - 'class' => $className - )); + /** @var $item \SplFileInfo */ + if (preg_match('/(Version(\d+))\.php/', $item->getFilename(), $matches)) { + $applied = $this->migrationVersionTable->applied($matches[2]); + if ($all || !$applied) { + $className = $this->namespaceMigrationsClasses . '\\' . $matches[1]; + + if (!class_exists($className)) + require_once $this->migrationClassFolder . '/' . $item->getFilename(); + + if (class_exists($className)) { + $reflectionClass = new \ReflectionClass($className); + $reflectionDescription = new \ReflectionProperty($className, 'description'); + + if ($reflectionClass->implementsInterface('ZendDbMigrations\Library\MigrationInterface')) { + $classes->append(array( + 'version' => $matches[2], + 'class' => $className, + 'description' => $reflectionDescription->getValue(), + 'applied' => $applied, + )); + } } } } } - - $classes->uasort(function($a, $b){ + + $classes->uasort(function ($a, $b) { if ($a['version'] == $b['version']) { return 0; } - + return ($a['version'] < $b['version']) ? -1 : 1; }); - + return $classes; } + + protected function applyMigration(array $migration, $down = false) + { + /** @var $migrationObject AbstractMigration */ + $migrationObject = new $migration['class']($this->metadata); + + $this->outputWriter->write(sprintf("Execute migration class %s %s", $migration['class'], $down ? 'down' : 'up')); + + $sqlList = $down ? $migrationObject->getDownSql() : $migrationObject->getUpSql(); + foreach ($sqlList as $sql) { + $this->outputWriter->write("Execute sql code \n\n" . $sql . "\n"); + $this->connection->execute($sql); + } + + if ($down) { + $this->migrationVersionTable->delete($migration['version']); + } else { + $this->migrationVersionTable->save($migration['version']); + } + } } ?> diff --git a/src/ZendDbMigrations/Model/MigrationVersion.php b/src/ZendDbMigrations/Model/MigrationVersion.php new file mode 100644 index 0000000..6620926 --- /dev/null +++ b/src/ZendDbMigrations/Model/MigrationVersion.php @@ -0,0 +1,60 @@ +{$property} = (isset($data[$property])) ? $data[$property] : null; + } + } + + /** + * @param int $id + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @param int $version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * @return int + */ + public function getVersion() + { + return $this->version; + } +} \ No newline at end of file diff --git a/src/ZendDbMigrations/Model/MigrationVersionTable.php b/src/ZendDbMigrations/Model/MigrationVersionTable.php new file mode 100644 index 0000000..d611542 --- /dev/null +++ b/src/ZendDbMigrations/Model/MigrationVersionTable.php @@ -0,0 +1,49 @@ +tableGateway = $tableGateway; + } + + public function save($version) + { + $this->tableGateway->insert(array('version' => $version)); + return $this->tableGateway->lastInsertValue; + } + + public function delete($version) + { + $this->tableGateway->delete(array('version' => $version)); + } + + public function applied($version) + { + $result = $this->tableGateway->select(array('version' => $version)); + return $result->count() > 0; + } + + public function getCurrentVersion() + { + $result = $this->tableGateway->select(function ($select) { + $select->order('version DESC')->limit(1); + }); + if (!$result->count()) return 0; + return $result->current()->getVersion(); + } +} \ No newline at end of file