/
home
/
obinna
/
html
/
boazapp
/
vendor
/
doctrine
/
migrations
/
lib
/
Doctrine
/
Migrations
/
Metadata
/
Storage
/
Upload File
HOME
<?php declare(strict_types=1); namespace Doctrine\Migrations\Metadata\Storage; use DateTimeImmutable; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Types; use Doctrine\Migrations\Exception\MetadataStorageError; use Doctrine\Migrations\Metadata\AvailableMigration; use Doctrine\Migrations\Metadata\ExecutedMigration; use Doctrine\Migrations\Metadata\ExecutedMigrationsList; use Doctrine\Migrations\MigrationsRepository; use Doctrine\Migrations\Version\Comparator as MigrationsComparator; use Doctrine\Migrations\Version\Direction; use Doctrine\Migrations\Version\ExecutionResult; use Doctrine\Migrations\Version\Version; use InvalidArgumentException; use function array_change_key_case; use function floatval; use function method_exists; use function round; use function sprintf; use function strlen; use function strpos; use function strtolower; use function uasort; use const CASE_LOWER; final class TableMetadataStorage implements MetadataStorage { /** @var bool */ private $isInitialized; /** @var bool */ private $schemaUpToDate = false; /** @var Connection */ private $connection; /** @var AbstractSchemaManager<AbstractPlatform> */ private $schemaManager; /** @var AbstractPlatform */ private $platform; /** @var TableMetadataStorageConfiguration */ private $configuration; /** @var MigrationsRepository|null */ private $migrationRepository; /** @var MigrationsComparator */ private $comparator; public function __construct( Connection $connection, MigrationsComparator $comparator, ?MetadataStorageConfiguration $configuration = null, ?MigrationsRepository $migrationRepository = null ) { $this->migrationRepository = $migrationRepository; $this->connection = $connection; $this->schemaManager = $connection->getSchemaManager(); $this->platform = $connection->getDatabasePlatform(); if ($configuration !== null && ! ($configuration instanceof TableMetadataStorageConfiguration)) { throw new InvalidArgumentException(sprintf('%s accepts only %s as configuration', self::class, TableMetadataStorageConfiguration::class)); } $this->configuration = $configuration ?? new TableMetadataStorageConfiguration(); $this->comparator = $comparator; } public function getExecutedMigrations(): ExecutedMigrationsList { if (! $this->isInitialized()) { return new ExecutedMigrationsList([]); } $this->checkInitialization(); $rows = $this->connection->fetchAllAssociative(sprintf('SELECT * FROM %s', $this->configuration->getTableName())); $migrations = []; foreach ($rows as $row) { $row = array_change_key_case($row, CASE_LOWER); $version = new Version($row[strtolower($this->configuration->getVersionColumnName())]); $executedAt = $row[strtolower($this->configuration->getExecutedAtColumnName())] ?? ''; $executedAt = $executedAt !== '' ? DateTimeImmutable::createFromFormat($this->platform->getDateTimeFormatString(), $executedAt) : null; $executionTime = isset($row[strtolower($this->configuration->getExecutionTimeColumnName())]) ? floatval($row[strtolower($this->configuration->getExecutionTimeColumnName())] / 1000) : null; $migration = new ExecutedMigration( $version, $executedAt instanceof DateTimeImmutable ? $executedAt : null, $executionTime ); $migrations[(string) $version] = $migration; } uasort($migrations, function (ExecutedMigration $a, ExecutedMigration $b): int { return $this->comparator->compare($a->getVersion(), $b->getVersion()); }); return new ExecutedMigrationsList($migrations); } public function reset(): void { $this->checkInitialization(); $this->connection->executeStatement( sprintf( 'DELETE FROM %s WHERE 1 = 1', $this->platform->quoteIdentifier($this->configuration->getTableName()) ) ); } public function complete(ExecutionResult $result): void { $this->checkInitialization(); if ($result->getDirection() === Direction::DOWN) { $this->connection->delete($this->configuration->getTableName(), [ $this->configuration->getVersionColumnName() => (string) $result->getVersion(), ]); } else { $this->connection->insert($this->configuration->getTableName(), [ $this->configuration->getVersionColumnName() => (string) $result->getVersion(), $this->configuration->getExecutedAtColumnName() => $result->getExecutedAt(), $this->configuration->getExecutionTimeColumnName() => $result->getTime() === null ? null : (int) round($result->getTime() * 1000), ], [ Types::STRING, Types::DATETIME_MUTABLE, Types::INTEGER, ]); } } public function ensureInitialized(): void { if (! $this->isInitialized()) { $expectedSchemaChangelog = $this->getExpectedTable(); $this->schemaManager->createTable($expectedSchemaChangelog); $this->schemaUpToDate = true; $this->isInitialized = true; return; } $this->isInitialized = true; $expectedSchemaChangelog = $this->getExpectedTable(); $diff = $this->needsUpdate($expectedSchemaChangelog); if ($diff === null) { $this->schemaUpToDate = true; return; } $this->schemaUpToDate = true; $this->schemaManager->alterTable($diff); $this->updateMigratedVersionsFromV1orV2toV3(); } private function needsUpdate(Table $expectedTable): ?TableDiff { if ($this->schemaUpToDate) { return null; } $comparator = method_exists($this->schemaManager, 'createComparator') ? $this->schemaManager->createComparator() : new Comparator(); $currentTable = $this->schemaManager->listTableDetails($this->configuration->getTableName()); $diff = $comparator->diffTable($currentTable, $expectedTable); return $diff instanceof TableDiff ? $diff : null; } private function isInitialized(): bool { if ($this->isInitialized) { return $this->isInitialized; } if ($this->connection instanceof PrimaryReadReplicaConnection) { $this->connection->ensureConnectedToPrimary(); } return $this->schemaManager->tablesExist([$this->configuration->getTableName()]); } private function checkInitialization(): void { if (! $this->isInitialized()) { throw MetadataStorageError::notInitialized(); } $expectedTable = $this->getExpectedTable(); if ($this->needsUpdate($expectedTable) !== null) { throw MetadataStorageError::notUpToDate(); } } private function getExpectedTable(): Table { $schemaChangelog = new Table($this->configuration->getTableName()); $schemaChangelog->addColumn( $this->configuration->getVersionColumnName(), 'string', ['notnull' => true, 'length' => $this->configuration->getVersionColumnLength()] ); $schemaChangelog->addColumn($this->configuration->getExecutedAtColumnName(), 'datetime', ['notnull' => false]); $schemaChangelog->addColumn($this->configuration->getExecutionTimeColumnName(), 'integer', ['notnull' => false]); $schemaChangelog->setPrimaryKey([$this->configuration->getVersionColumnName()]); return $schemaChangelog; } private function updateMigratedVersionsFromV1orV2toV3(): void { if ($this->migrationRepository === null) { return; } $availableMigrations = $this->migrationRepository->getMigrations()->getItems(); $executedMigrations = $this->getExecutedMigrations()->getItems(); foreach ($availableMigrations as $availableMigration) { foreach ($executedMigrations as $k => $executedMigration) { if ($this->isAlreadyV3Format($availableMigration, $executedMigration)) { continue; } $this->connection->update( $this->configuration->getTableName(), [ $this->configuration->getVersionColumnName() => (string) $availableMigration->getVersion(), ], [ $this->configuration->getVersionColumnName() => (string) $executedMigration->getVersion(), ] ); unset($executedMigrations[$k]); } } } private function isAlreadyV3Format(AvailableMigration $availableMigration, ExecutedMigration $executedMigration): bool { return strpos( (string) $availableMigration->getVersion(), (string) $executedMigration->getVersion() ) !== strlen((string) $availableMigration->getVersion()) - strlen((string) $executedMigration->getVersion()); } }