-
Notifications
You must be signed in to change notification settings - Fork 6
Issue #430: PostgreSQL implementation #462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Core\App\Doctrine; | ||
|
|
||
| use Core\App\DBAL\Types\AbstractEnumType; | ||
| use Doctrine\Common\EventSubscriber; | ||
| use Doctrine\DBAL\Connection; | ||
| use Doctrine\DBAL\Exception; | ||
| use Doctrine\DBAL\Platforms\PostgreSQLPlatform; | ||
| use Doctrine\Migrations\Events; | ||
| use Doctrine\ORM\EntityManagerInterface; | ||
| use Psr\Container\ContainerExceptionInterface; | ||
| use Psr\Container\ContainerInterface; | ||
| use Psr\Container\NotFoundExceptionInterface; | ||
|
|
||
| use function array_column; | ||
| use function array_key_exists; | ||
| use function implode; | ||
| use function in_array; | ||
| use function sprintf; | ||
|
|
||
| class MigrationsMigratedSubscriber implements EventSubscriber | ||
| { | ||
| private EntityManagerInterface $entityManager; | ||
| private Connection $connection; | ||
|
|
||
| /** | ||
| * @throws ContainerExceptionInterface | ||
| * @throws NotFoundExceptionInterface | ||
| */ | ||
| public function __construct( | ||
| private readonly ContainerInterface $container, | ||
| ) { | ||
| $this->entityManager = $container->get('doctrine.entity_manager.orm_default'); | ||
| $this->connection = $this->entityManager->getConnection(); | ||
| } | ||
|
|
||
| /** | ||
| * @throws Exception | ||
| */ | ||
| public function getSubscribedEvents(): array | ||
| { | ||
| if (! $this->connection->getDatabasePlatform() instanceof PostgreSQLPlatform) { | ||
| return []; | ||
| } | ||
|
|
||
| return [ | ||
| Events::onMigrationsMigrating, | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * @throws ContainerExceptionInterface | ||
| * @throws NotFoundExceptionInterface | ||
| * @throws Exception | ||
| */ | ||
| public function onMigrationsMigrating(): void | ||
|
Check warning on line 59 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| { | ||
| $dbEnumTypes = $this->getCustomEnumTypesFromTheDatabase(); | ||
| $fsEnumTypes = $this->getCustomEnumTypesFromTheFileSystem(); | ||
|
|
||
| $enumTypes = $this->mergeCustomEnumTypes($dbEnumTypes, $fsEnumTypes); | ||
| foreach ($enumTypes as $action => $enums) { | ||
| foreach ($enums as $type => $values) { | ||
| match ($action) { | ||
| 'create' => $this->createDatabaseType($type, $values), | ||
| 'delete' => $this->deleteDatabaseType($type), | ||
|
Check warning on line 69 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| 'update' => $this->updateDatabaseType($type, $values), | ||
| default => null, | ||
| }; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @phpstan-return array<non-empty-string, AbstractEnumType> | ||
| * @throws ContainerExceptionInterface | ||
| * @throws NotFoundExceptionInterface | ||
| */ | ||
| private function getCustomEnumTypesFromTheFileSystem(): array | ||
| { | ||
| $enumTypes = []; | ||
|
|
||
| $customTypes = $this->container->get('config')['doctrine']['types'] ?? []; | ||
| foreach ($customTypes as $type => $class) { | ||
| $class = new $class(); | ||
| if (! $class instanceof AbstractEnumType) { | ||
| continue; | ||
| } | ||
| $enumTypes[$type] = $class; | ||
| } | ||
|
|
||
| return $enumTypes; | ||
| } | ||
|
|
||
| /** | ||
| * @phpstan-return list<non-empty-string> | ||
| * @throws Exception | ||
| */ | ||
| private function getDatabaseTypeValues(string $type): array | ||
|
Check warning on line 102 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| { | ||
| $results = $this->connection->executeQuery( | ||
| "SELECT e.enumlabel FROM pg_type t JOIN pg_enum e ON t.oid = e.enumtypid WHERE t.typname = '$type';" | ||
|
Check warning on line 105 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| )->fetchAllAssociative(); | ||
|
|
||
| return array_column($results, 'enumlabel'); | ||
| } | ||
|
|
||
| /** | ||
| * @return list<non-empty-string> | ||
| * @throws Exception | ||
| */ | ||
| private function getCustomEnumTypesFromTheDatabase(): array | ||
| { | ||
| return $this->connection->executeQuery( | ||
| 'SELECT t.typname FROM pg_type t JOIN pg_enum e ON t.oid = e.enumtypid GROUP BY t.typname' | ||
|
Check warning on line 118 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| )->fetchFirstColumn(); | ||
| } | ||
|
|
||
| /** | ||
| * @param list<non-empty-string> $dbEnumTypes | ||
| * @param array<non-empty-string, AbstractEnumType> $fsEnumTypes | ||
| * @return array{ | ||
| * create: array<non-empty-string, list<non-empty-string>>, | ||
| * delete: array<non-empty-string, list<non-empty-string>>, | ||
| * skip: array<non-empty-string, list<non-empty-string>>, | ||
| * update: array<non-empty-string, list<non-empty-string>> | ||
| * } | ||
| * @throws Exception | ||
| */ | ||
| private function mergeCustomEnumTypes(array $dbEnumTypes, array $fsEnumTypes): array | ||
| { | ||
| $enumTypes = [ | ||
| 'create' => [], | ||
| 'delete' => [], | ||
| 'skip' => [], | ||
| 'update' => [], | ||
| ]; | ||
|
|
||
| /** @var AbstractEnumType $class */ | ||
| foreach ($fsEnumTypes as $type => $class) { | ||
| $fsTypeValues = $class->getEnumValues(); | ||
| if (in_array($type, $dbEnumTypes)) { | ||
| $dbTypeValues = $this->getDatabaseTypeValues($type); | ||
|
Check warning on line 146 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| if ($dbTypeValues === $fsTypeValues) { | ||
| $enumTypes['skip'][$type] = $fsTypeValues; | ||
| } else { | ||
| $enumTypes['update'][$type] = $fsTypeValues; | ||
| } | ||
| } else { | ||
| $enumTypes['create'][$type] = $fsTypeValues; | ||
| } | ||
| } | ||
|
|
||
| foreach ($dbEnumTypes as $type) { | ||
| if (! array_key_exists($type, $fsEnumTypes)) { | ||
| $enumTypes['delete'][$type] = $this->getDatabaseTypeValues($type); | ||
| } | ||
| } | ||
|
|
||
| return $enumTypes; | ||
| } | ||
|
|
||
| /** | ||
| * @param non-empty-string $type | ||
| * @param list<non-empty-string> $values | ||
| * @throws Exception | ||
| */ | ||
| private function createDatabaseType(string $type, array $values): void | ||
|
Check warning on line 171 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| { | ||
| $this->connection->executeQuery( | ||
| sprintf("CREATE TYPE %s AS ENUM ('%s');", $type, implode("', '", $values)) | ||
|
Check warning on line 174 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @throws Exception | ||
| */ | ||
| private function deleteDatabaseType(string $type): void | ||
|
Check warning on line 181 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| { | ||
| $this->connection->executeQuery( | ||
| sprintf('DROP TYPE %s;', $type) | ||
|
Check warning on line 184 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @param non-empty-string $type | ||
| * @param list<non-empty-string> $values | ||
| * @throws Exception | ||
| */ | ||
| private function updateDatabaseType(string $type, array $values): void | ||
|
Check warning on line 193 in src/Core/src/App/src/Doctrine/MigrationsMigratedSubscriber.php
|
||
| { | ||
| $this->deleteDatabaseType($type); | ||
| $this->createDatabaseType($type, $values); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.