vendor/doctrine/migrations/lib/Doctrine/Migrations/Version/SortedMigrationPlanCalculator.php line 83

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Migrations\Version;
  4. use Doctrine\Migrations\Exception\MigrationClassNotFound;
  5. use Doctrine\Migrations\Metadata;
  6. use Doctrine\Migrations\Metadata\AvailableMigration;
  7. use Doctrine\Migrations\Metadata\AvailableMigrationsList;
  8. use Doctrine\Migrations\Metadata\ExecutedMigrationsList;
  9. use Doctrine\Migrations\Metadata\MigrationPlan;
  10. use Doctrine\Migrations\Metadata\MigrationPlanList;
  11. use Doctrine\Migrations\Metadata\Storage\MetadataStorage;
  12. use Doctrine\Migrations\MigrationsRepository;
  13. use function array_diff;
  14. use function array_filter;
  15. use function array_map;
  16. use function array_reverse;
  17. use function count;
  18. use function in_array;
  19. use function reset;
  20. use function uasort;
  21. /**
  22.  * The MigrationPlanCalculator is responsible for calculating the plan for migrating from the current
  23.  * version to another version.
  24.  *
  25.  * @internal
  26.  */
  27. final class SortedMigrationPlanCalculator implements MigrationPlanCalculator
  28. {
  29.     public function __construct(
  30.         private readonly MigrationsRepository $migrationRepository,
  31.         private readonly MetadataStorage $metadataStorage,
  32.         private readonly Comparator $sorter,
  33.     ) {
  34.     }
  35.     /** @param Version[] $versions */
  36.     public function getPlanForVersions(array $versionsstring $direction): MigrationPlanList
  37.     {
  38.         $migrationsToCheck   $this->arrangeMigrationsForDirection($direction$this->getMigrations());
  39.         $availableMigrations array_filter(
  40.             $migrationsToCheck,
  41.             // in_array third parameter is intentionally false to force object to string casting
  42.             static fn (AvailableMigration $availableMigration): bool => in_array($availableMigration->getVersion(), $versionsfalse)
  43.         );
  44.         $planItems array_map(static fn (AvailableMigration $availableMigration): MigrationPlan => new MigrationPlan($availableMigration->getVersion(), $availableMigration->getMigration(), $direction), $availableMigrations);
  45.         if (count($planItems) !== count($versions)) {
  46.             $plannedVersions array_map(static fn (MigrationPlan $migrationPlan): Version => $migrationPlan->getVersion(), $planItems);
  47.             $diff            array_diff($versions$plannedVersions);
  48.             throw MigrationClassNotFound::new((string) reset($diff));
  49.         }
  50.         return new MigrationPlanList($planItems$direction);
  51.     }
  52.     public function getPlanUntilVersion(Version $to): MigrationPlanList
  53.     {
  54.         if ((string) $to !== '0' && ! $this->migrationRepository->hasMigration((string) $to)) {
  55.             throw MigrationClassNotFound::new((string) $to);
  56.         }
  57.         $availableMigrations $this->getMigrations(); // migrations are sorted at this point
  58.         $executedMigrations  $this->metadataStorage->getExecutedMigrations();
  59.         $direction $this->findDirection($to$executedMigrations$availableMigrations);
  60.         $migrationsToCheck $this->arrangeMigrationsForDirection($direction$availableMigrations);
  61.         $toExecute $this->findMigrationsToExecute($to$migrationsToCheck$direction$executedMigrations);
  62.         return new MigrationPlanList(array_map(static fn (AvailableMigration $migration): MigrationPlan => new MigrationPlan($migration->getVersion(), $migration->getMigration(), $direction), $toExecute), $direction);
  63.     }
  64.     public function getMigrations(): AvailableMigrationsList
  65.     {
  66.         $availableMigrations $this->migrationRepository->getMigrations()->getItems();
  67.         uasort($availableMigrations, fn (AvailableMigration $aAvailableMigration $b): int => $this->sorter->compare($a->getVersion(), $b->getVersion()));
  68.         return new AvailableMigrationsList($availableMigrations);
  69.     }
  70.     private function findDirection(Version $toExecutedMigrationsList $executedMigrationsAvailableMigrationsList $availableMigrations): string
  71.     {
  72.         if ((string) $to === '0') {
  73.             return Direction::DOWN;
  74.         }
  75.         foreach ($availableMigrations->getItems() as $availableMigration) {
  76.             if ($availableMigration->getVersion()->equals($to)) {
  77.                 break;
  78.             }
  79.             if (! $executedMigrations->hasMigration($availableMigration->getVersion())) {
  80.                 return Direction::UP;
  81.             }
  82.         }
  83.         if ($executedMigrations->hasMigration($to) && ! $executedMigrations->getLast()->getVersion()->equals($to)) {
  84.             return Direction::DOWN;
  85.         }
  86.         return Direction::UP;
  87.     }
  88.     /** @return  AvailableMigration[] */
  89.     private function arrangeMigrationsForDirection(string $directionMetadata\AvailableMigrationsList $availableMigrations): array
  90.     {
  91.         return $direction === Direction::UP $availableMigrations->getItems() : array_reverse($availableMigrations->getItems());
  92.     }
  93.     /**
  94.      * @param AvailableMigration[] $migrationsToCheck
  95.      *
  96.      * @return AvailableMigration[]
  97.      */
  98.     private function findMigrationsToExecute(Version $to, array $migrationsToCheckstring $directionExecutedMigrationsList $executedMigrations): array
  99.     {
  100.         $toExecute = [];
  101.         foreach ($migrationsToCheck as $availableMigration) {
  102.             if ($direction === Direction::DOWN && $availableMigration->getVersion()->equals($to)) {
  103.                 break;
  104.             }
  105.             if ($direction === Direction::UP && ! $executedMigrations->hasMigration($availableMigration->getVersion())) {
  106.                 $toExecute[] = $availableMigration;
  107.             } elseif ($direction === Direction::DOWN && $executedMigrations->hasMigration($availableMigration->getVersion())) {
  108.                 $toExecute[] = $availableMigration;
  109.             }
  110.             if ($direction === Direction::UP && $availableMigration->getVersion()->equals($to)) {
  111.                 break;
  112.             }
  113.         }
  114.         return $toExecute;
  115.     }
  116. }