Passed
Pull Request — master (#969)
by Asmir
01:58
created

SortedMigrationPlanCalculator::getMigrations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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