Skip to content

Commit

Permalink
migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
mvkasatkin committed Nov 8, 2017
1 parent 82ad848 commit aad44ca
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 2 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"doctrine/dbal": "^2.5",
"symfony/cache": "^3.3",
"symfony/finder": "^3.3",
"symfony/console": "^3.3",
"hanneskod/classtools": "~1.0"
},
"require-dev": {
Expand Down
4 changes: 4 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<filter>
<whitelist>
<directory suffix=".php">./src</directory>
<exclude>
<file>./src/utils/migration/commands/MigrationUpCommand.php</file>
<file>./src/utils/migration/commands/MigrationDownCommand.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>
8 changes: 8 additions & 0 deletions src/cube/AbstractCube.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,12 @@ abstract class AbstractCube
* @return void
*/
abstract public function registerDependencies(array &$definitions);

/**
* @return array [sort => migration class]
*/
public function getMigrations(): array
{
return [];
}
}
8 changes: 8 additions & 0 deletions src/cube/CubeManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ public function getCube($cubeClassName): AbstractCube
return $this->registered[$cubeClassName];
}

/**
* @return AbstractCube[]
*/
public function getCubes(): array
{
return $this->registered;
}

/**
* @param $cubeClassName
* @param array $definitions
Expand Down
18 changes: 18 additions & 0 deletions src/utils/migration/MigrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,22 @@ public function down($class)
{
$this->registry->unregister($class);
}

/**
* @return array
*/
public function getRegistered(): array
{
return $this->registry->getRegistered();
}

/**
* @param string $class
*
* @return bool
*/
public function isRegistered(string $class): bool
{
return $this->registry->isRegistered($class);
}
}
87 changes: 87 additions & 0 deletions src/utils/migration/commands/MigrationDownCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace WebComplete\core\utils\migration\commands;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use WebComplete\core\cube\CubeManager;
use WebComplete\core\utils\migration\MigrationService;

class MigrationDownCommand extends Command
{

/**
* @var MigrationService
*/
protected $migrationService;

/**
* @param MigrationService $migrationService
*
* @throws \Symfony\Component\Console\Exception\LogicException
*/
public function __construct(MigrationService $migrationService)
{
parent::__construct();
$this->migrationService = $migrationService;
}

/**
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
*/
protected function configure()
{
$this->setName('migrate:down')
->setDescription('Rollback migration[s]')
->addArgument('class', InputArgument::OPTIONAL, 'Migration class');
}

/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return null|int
* @throws \Symfony\Component\Console\Exception\LogicException
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($class = $input->getArgument('class')) {
$rollbackMigrations = [$class];
} else {
$rollbackMigrations = $this->migrationService->getRegistered();
$rollbackMigrations = \array_reverse($rollbackMigrations, true);
}

if (!$rollbackMigrations) {
$output->writeln('Migrations not found');
return null;
}

$message = "Rollback migrations: \n";
foreach ($rollbackMigrations as $migrationClass) {
$message .= $migrationClass . "\n";
}
$output->writeln($message . "\n");

$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Continue? (Y/N): ', false);
if ($class || $helper->ask($input, $output, $question)) {
foreach ($rollbackMigrations as $migrationClass) {
$output->writeln('Rollback: ' . $migrationClass);
$this->migrationService->down($migrationClass);
}
$output->writeln('Done!');
return null;
}

$output->writeln('Cancel');
return null;
}
}
115 changes: 115 additions & 0 deletions src/utils/migration/commands/MigrationUpCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace WebComplete\core\utils\migration\commands;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use WebComplete\core\cube\CubeManager;
use WebComplete\core\utils\migration\MigrationService;

class MigrationUpCommand extends Command
{

/**
* @var CubeManager
*/
protected $cubeManager;
/**
* @var MigrationService
*/
protected $migrationService;

/**
* @param CubeManager $cubeManager
* @param MigrationService $migrationService
*
* @throws \Symfony\Component\Console\Exception\LogicException
*/
public function __construct(CubeManager $cubeManager, MigrationService $migrationService)
{
parent::__construct();
$this->cubeManager = $cubeManager;
$this->migrationService = $migrationService;
}

/**
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
*/
protected function configure()
{
$this->setName('migrate:up')
->setDescription('Execute migration[s]')
->addArgument('class', InputArgument::OPTIONAL, 'Migration class');
}

/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return null|int
* @throws \Symfony\Component\Console\Exception\LogicException
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($class = $input->getArgument('class')) {
$newMigrations = [$class];
} else {
$newMigrations = $this->getNewMigrations();
}

if (!$newMigrations) {
$output->writeln('New migrations not found');
return null;
}

$message = "New migrations: \n";
foreach ($newMigrations as $migrationClass) {
$message .= $migrationClass . "\n";
}
$output->writeln($message . "\n");

$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Continue? (Y/N): ', false);
if ($class || $helper->ask($input, $output, $question)) {
foreach ($newMigrations as $migrationClass) {
$output->writeln('Run: ' . $migrationClass);
$this->migrationService->up($migrationClass);
}
$output->writeln('Done!');
return null;
}

$output->writeln('Cancel');
return null;
}

/**
* @return array
*/
protected function getNewMigrations(): array
{
$cubes = $this->cubeManager->getCubes();
$migrations = [[]];
foreach ($cubes as $cube) {
$migrations[] = $cube->getMigrations();
}
$allMigrations = \array_merge(...$migrations);
\ksort($allMigrations, \SORT_STRING & \SORT_ASC);

$newMigrations = [];
foreach ($allMigrations as $k => $migrationClass) {
if (!$this->migrationService->isRegistered($migrationClass)) {
$newMigrations[$k] = $migrationClass;
}
}

return $newMigrations;
}
}
16 changes: 14 additions & 2 deletions tests/core/cube/CubeManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function testInstance()
$this->assertInstanceOf(CubeManager::class, $pm);
}

public function testGetPackage()
public function testGetCube()
{
$p = Mocker::create(AbstractCube::class);
$classHelper = new ClassHelper();
Expand All @@ -26,7 +26,19 @@ public function testGetPackage()
$this->assertInstanceOf(get_class($p), $pm->getCube(get_class($p)));
}

public function testGetPackageException()
public function testGetCubes()
{
$p = Mocker::create(AbstractCube::class);
$classHelper = new ClassHelper();
$pm = new CubeManager($classHelper, new NullCache());
$def = [];
$pm->register(get_class($p), $def);
$this->assertEquals([
get_class($p) => $p
], $pm->getCubes());
}

public function testGetCubeException()
{
$this->expectException(CubeException::class);
$classHelper = new ClassHelper();
Expand Down
16 changes: 16 additions & 0 deletions tests/core/cube/CubeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace WebComplete\core\cube;

use Mvkasatkin\mocker\Mocker;

class CubeTest extends \CoreTestCase
{

public function testGetMigrations()
{
/** @var AbstractCube $cube */
$cube = Mocker::create(AbstractCube::class);
$this->assertEquals([], $cube->getMigrations());
}
}
20 changes: 20 additions & 0 deletions tests/core/utils/migration/MigrationServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,26 @@ public function testNotAMigration()
$migrationRegistry->register('testClass');
}

public function testGetRegistered()
{
/** @var MigrationRegistryInterface $registry */
$registry = Mocker::create(MigrationRegistryInterface::class, [
Mocker::method('getRegistered', 1)->returns([]),
]);
$service = new MigrationService($registry);
$this->assertEquals([], $service->getRegistered());
}

public function testIsRegistered()
{
/** @var MigrationRegistryInterface $registry */
$registry = Mocker::create(MigrationRegistryInterface::class, [
Mocker::method('isRegistered', 1, 'aaa')->returns(true),
]);
$service = new MigrationService($registry);
$this->assertTrue($service->isRegistered('aaa'));
}

/**
* @return MigrationRegistryMysql
*/
Expand Down

0 comments on commit aad44ca

Please sign in to comment.