Completed
Push — master ( 1f054f...39d367 )
by Grégoire
02:27
created

SortedMigrationPlanCalculator   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Test Coverage

Coverage 97.73%

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 103
ccs 43
cts 44
cp 0.9773
rs 10
c 0
b 0
f 0
wmc 22

6 Methods

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