Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
loperd committed May 31, 2023
0 parents commit 29bb9a6
Show file tree
Hide file tree
Showing 13 changed files with 647 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor/
composer.lock
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Highcore Doctrine Migrations Bundle

## Installation
```shell
composer require highcore/doctrine-migrations-bundle
```

## Usage

To create SQL files with migration, use:
```shell
./bin/console doctrine:migrations:generate
```

To create a diff SQL migration with your actual database:
```shell
./bin/console doctrine:migrations:diff
```

## Example
```
$ tree
.
├── sql
│ ├── Version20220708173033_up.sql
│ ├── Version20220708173033_down.sql
│ ├── Version20220710115549_up.sql
│ ├── Version20220710115549_down.sql
├── Version20220708173033.php
├── Version20220710115549.php
```

> You can delete `_down.sql` migration if you don't need it.
30 changes: 30 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "highcore/doctrine-migration-bundle",
"description": "Doctrine migrations Symfony bundle.",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"Highcore\\DoctrineMigrationBundle\\": "src/"
}
},
"authors": [
{
"name": "Roman",
"email": "[email protected]"
},
{
"name": "Highcore Team",
"homepage": "https://highcore.org/team"
}
],
"require": {
"php": "^8.1 || ^8.2",
"doctrine/doctrine-bundle": "^2.9",
"symfony/console": "^5.4 || ^6.0",
"symfony/dependency-injection": "^5.4 || ^6.0",
"doctrine/migrations": "^3.6",
"symfony/config": "^5.4 || ^6.0",
"symfony/http-kernel": "^5.4 || ^6.0"
}
}
65 changes: 65 additions & 0 deletions src/Command/DecoratesDoctrineCommandTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle\Command;

use Doctrine\Migrations\Configuration\Configuration;
use Doctrine\Migrations\DependencyFactory;
use Doctrine\Migrations\Tools\Console\Command\DoctrineCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

trait DecoratesDoctrineCommandTrait
{
private ?DependencyFactory $dependencyFactory = null;

abstract protected function getOriginalCommand(): DoctrineCommand;

public function getDependencyFactory(): DependencyFactory
{
/** @var DependencyFactory */
return $this->dependencyFactory ?? ($this->dependencyFactory = $this->call('getDependencyFactory'));
}

public function getConfiguration(): Configuration
{
return $this->getDependencyFactory()->getConfiguration();
}

protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->call(__FUNCTION__, $input, $output);
}
protected function configure(): void
{
$this->setDefinition($this->getOriginalCommand()->getNativeDefinition());

$this->call(__FUNCTION__);
}

private function resetFreeze(DependencyFactory $dependencyFactory): void
{
$reflection = new \ReflectionObject($dependencyFactory);
$property = $reflection->getProperty('frozen');
$property->setValue($dependencyFactory, false);
}

/**
* @param mixed ...$args
*
* @return mixed
*
* @throws \ReflectionException
*/
private function call(string $methodName, ...$args): mixed
{
$reflection = new \ReflectionObject($this->getOriginalCommand());
$method = $reflection->getMethod($methodName);

/** @noinspection PhpExpressionResultUnusedInspection */
$method->setAccessible(true);

return $method->invoke($this->getOriginalCommand(), ...$args);
}
}
64 changes: 64 additions & 0 deletions src/Command/DoctrineMigrationCreateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php /** @noinspection PhpInternalEntityUsedInspection */

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle\Command;

use Doctrine\Migrations\Generator\Generator as OriginalGenerator;
use Doctrine\Migrations\Generator\SqlGenerator as OriginalSqlGenerator;
use Doctrine\Migrations\Tools\Console\Command\DoctrineCommand;
use Doctrine\Migrations\Tools\Console\Command\GenerateCommand;
use Highcore\DoctrineMigrationBundle\Migration\Generator\Generator;
use Highcore\DoctrineMigrationBundle\Migration\Generator\SqlGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Config\FileLocator;

final class DoctrineMigrationCreateCommand extends Command
{
use DecoratesDoctrineCommandTrait;

public function __construct(
private readonly FileLocator $fileLocator,
private readonly GenerateCommand $originalCommand,
) {
parent::__construct();
}

protected function getOriginalCommand(): DoctrineCommand
{
return $this->originalCommand;
}

/**
* @psalm-suppress InternalClass
* @psalm-suppress InternalMethod
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$dependencyFactory = $this->getDependencyFactory();
$configuration = $dependencyFactory->getConfiguration();

$this->resetFreeze($dependencyFactory);
$dependencyFactory->setDefinition(OriginalGenerator::class,
function () use ($dependencyFactory, $configuration): Generator {
return new Generator($configuration);
});

$this->resetFreeze($dependencyFactory);
$dependencyFactory->setDefinition(OriginalSqlGenerator::class,
function () use ($dependencyFactory): SqlGenerator {
return new SqlGenerator(
$configuration,
$dependencyFactory->getConnection()->getDatabasePlatform()
);
});

$customMigrationTemplate = $this->fileLocator->locate(
'@PersistenceBundle/Resources/tpl/migration.tpl');
$configuration->setCustomTemplate($customMigrationTemplate);

return $this->call(__FUNCTION__, $input, $output);
}
}
69 changes: 69 additions & 0 deletions src/Command/DoctrineMigrationDiffGeneratorCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php /** @noinspection PhpInternalEntityUsedInspection */

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle\Command;

use Doctrine\Migrations\Generator\Generator as OriginalGenerator;
use Doctrine\Migrations\Generator\SqlGenerator as OriginalSqlGenerator;
use Doctrine\Migrations\Tools\Console\Command\DiffCommand;
use Doctrine\Migrations\Tools\Console\Command\DoctrineCommand;
use Highcore\DoctrineMigrationBundle\Migration\Generator\Generator;
use Highcore\DoctrineMigrationBundle\Migration\Generator\SqlGenerator;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Config\FileLocator;

final class DoctrineMigrationDiffGeneratorCommand extends Command
{
use DecoratesDoctrineCommandTrait;
public function __construct(
private readonly FileLocator $fileLocator,
private readonly DiffCommand $originalCommand,
) {
parent::__construct();
}

protected function getOriginalCommand(): DoctrineCommand
{
return $this->originalCommand;
}

protected function configure(): void
{
$this->setDefinition($this->originalCommand->getNativeDefinition());

$this->call(__FUNCTION__);
}

protected function execute(
InputInterface $input,
OutputInterface $output
): int {
$dependencyFactory = $this->getDependencyFactory();
$configuration = $dependencyFactory->getConfiguration();

$this->resetFreeze($dependencyFactory);
$dependencyFactory->setDefinition(OriginalGenerator::class,
function () use($dependencyFactory): Generator {
return new Generator($dependencyFactory->getConfiguration());
});

$this->resetFreeze($dependencyFactory);
$dependencyFactory->setDefinition(OriginalSqlGenerator::class,
function () use($dependencyFactory, $configuration): SqlGenerator {
return new SqlGenerator(
$configuration,
$dependencyFactory->getConnection()->getDatabasePlatform()
);
});

$customMigrationTemplate = $this->fileLocator->locate(
'@PersistenceBundle/Resources/tpl/migration.tpl');
$configuration->setCustomTemplate($customMigrationTemplate);

return $this->call(__FUNCTION__, $input, $output);
}
}
21 changes: 21 additions & 0 deletions src/DependencyInjection/DoctrineMigrationExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;

final class DoctrineMigrationExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new PhpFileLoader($container,
new FileLocator(__DIR__ . '/../Resources/config'));

$loader->load('services.php');
}
}
11 changes: 11 additions & 0 deletions src/DoctrineMigrationBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

final class DoctrineMigrationBundle extends Bundle
{
}
74 changes: 74 additions & 0 deletions src/Migration/AbstractMigration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Highcore\DoctrineMigrationBundle\Migration;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;
use Psr\Log\LoggerInterface;

abstract class AbstractMigration extends \Doctrine\Migrations\AbstractMigration
{
private string $fileName;
private string $rootDir;

public function __construct(Connection $connection, LoggerInterface $logger)
{
parent::__construct($connection, $logger);

$reflection = new \ReflectionClass(static::class);
$this->fileName = $reflection->getShortName();
$this->rootDir = \dirname($reflection->getFileName());
}

final public function up(Schema $schema): void
{
$this->beforeUp();
$this->exec($this->rootDir . '/sql/' . $this->fileName . '_up.sql');
$this->afterUp();
}

final public function down(Schema $schema): void
{
$this->beforeDown();
$this->exec($this->rootDir . '/sql/' . $this->fileName . '_down.sql');
$this->afterDown();
}

private function exec(string $filePath): void
{
if (!\file_exists($filePath)) {
return;
}

$file = \fopen($filePath, 'rb');

$buffer = '';
while (false !== ($line = \fgets($file))) {
$buffer .= $line = \trim($line);
if (';' === \mb_substr($line, -1)) {
$this->addSql($buffer);
$buffer = '';
}
}

\fclose($file);
}

public function beforeUp(): void
{
}

public function beforeDown(): void
{
}

public function afterUp(): void
{
}

public function afterDown(): void
{
}
}
Loading

0 comments on commit 29bb9a6

Please sign in to comment.