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

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.     private MigrationsRepository $migrationRepository;
  30.     private MetadataStorage $metadataStorage;
  31.     private Comparator $sorter;
  32.     public function __construct(
  33.         MigrationsRepository $migrationRepository,
  34.         MetadataStorage $metadataStorage,
  35.         Comparator $sorter
  36.     ) {
  37.         $this->migrationRepository $migrationRepository;
  38.         $this->metadataStorage     $metadataStorage;
  39.         $this->sorter              $sorter;
  40.     }
  41.     /**
  42.      * @param Version[] $versions
  43.      */
  44.     public function getPlanForVersions(array $versionsstring $direction): MigrationPlanList
  45.     {
  46.         $migrationsToCheck   $this->arrangeMigrationsForDirection($direction$this->getMigrations());
  47.         $availableMigrations array_filter($migrationsToCheck, static function (AvailableMigration $availableMigration) use ($versions): bool {
  48.             // in_array third parameter is intentionally false to force object to string casting
  49.             return in_array($availableMigration->getVersion(), $versionsfalse);
  50.         });
  51.         $planItems array_map(static function (AvailableMigration $availableMigration) use ($direction): MigrationPlan {
  52.             return new MigrationPlan($availableMigration->getVersion(), $availableMigration->getMigration(), $direction);
  53.         }, $availableMigrations);
  54.         if (count($planItems) !== count($versions)) {
  55.             $plannedVersions array_map(static function (MigrationPlan $migrationPlan): Version {
  56.                 return $migrationPlan->getVersion();
  57.             }, $planItems);
  58.             $diff            array_diff($versions$plannedVersions);
  59.             throw MigrationClassNotFound::new((string) reset($diff));
  60.         }
  61.         return new MigrationPlanList($planItems$direction);
  62.     }
  63.     public function getPlanUntilVersion(Version $to): MigrationPlanList
  64.     {
  65.         if ((string) $to !== '0' && ! $this->migrationRepository->hasMigration((string) $to)) {
  66.             throw MigrationClassNotFound::new((string) $to);
  67.         }
  68.         $availableMigrations $this->getMigrations(); // migrations are sorted at this point
  69.         $executedMigrations  $this->metadataStorage->getExecutedMigrations();
  70.         $direction $this->findDirection($to$executedMigrations$availableMigrations);
  71.         $migrationsToCheck $this->arrangeMigrationsForDirection($direction$availableMigrations);
  72.         $toExecute $this->findMigrationsToExecute($to$migrationsToCheck$direction$executedMigrations);
  73.         return new MigrationPlanList(array_map(static function (AvailableMigration $migration) use ($direction): MigrationPlan {
  74.             return new MigrationPlan($migration->getVersion(), $migration->getMigration(), $direction);
  75.         }, $toExecute), $direction);
  76.     }
  77.     public function getMigrations(): AvailableMigrationsList
  78.     {
  79.         $availableMigrations $this->migrationRepository->getMigrations()->getItems();
  80.         uasort($availableMigrations, function (AvailableMigration $aAvailableMigration $b): int {
  81.             return $this->sorter->compare($a->getVersion(), $b->getVersion());
  82.         });
  83.         return new AvailableMigrationsList($availableMigrations);
  84.     }
  85.     private function findDirection(Version $toExecutedMigrationsList $executedMigrationsAvailableMigrationsList $availableMigrations): string
  86.     {
  87.         if ((string) $to === '0') {
  88.             return Direction::DOWN;
  89.         }
  90.         foreach ($availableMigrations->getItems() as $availableMigration) {
  91.             if ($availableMigration->getVersion()->equals($to)) {
  92.                 break;
  93.             }
  94.             if (! $executedMigrations->hasMigration($availableMigration->getVersion())) {
  95.                 return Direction::UP;
  96.             }
  97.         }
  98.         if ($executedMigrations->hasMigration($to) && ! $executedMigrations->getLast()->getVersion()->equals($to)) {
  99.             return Direction::DOWN;
  100.         }
  101.         return Direction::UP;
  102.     }
  103.     /**
  104.      * @return  AvailableMigration[]
  105.      */
  106.     private function arrangeMigrationsForDirection(string $directionMetadata\AvailableMigrationsList $availableMigrations): array
  107.     {
  108.         return $direction === Direction::UP $availableMigrations->getItems() : array_reverse($availableMigrations->getItems());
  109.     }
  110.     /**
  111.      * @param AvailableMigration[] $migrationsToCheck
  112.      *
  113.      * @return AvailableMigration[]
  114.      */
  115.     private function findMigrationsToExecute(Version $to, array $migrationsToCheckstring $directionExecutedMigrationsList $executedMigrations): array
  116.     {
  117.         $toExecute = [];
  118.         foreach ($migrationsToCheck as $availableMigration) {
  119.             if ($direction === Direction::DOWN && $availableMigration->getVersion()->equals($to)) {
  120.                 break;
  121.             }
  122.             if ($direction === Direction::UP && ! $executedMigrations->hasMigration($availableMigration->getVersion())) {
  123.                 $toExecute[] = $availableMigration;
  124.             } elseif ($direction === Direction::DOWN && $executedMigrations->hasMigration($availableMigration->getVersion())) {
  125.                 $toExecute[] = $availableMigration;
  126.             }
  127.             if ($direction === Direction::UP && $availableMigration->getVersion()->equals($to)) {
  128.                 break;
  129.             }
  130.         }
  131.         return $toExecute;
  132.     }
  133. }