Completed
Push — master ( 0854ed...7daeea )
by Jonathan
01:35 queued 01:34
created

removeMigrationVersionFromDatabase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\Migrations\Configuration\Configuration;
9
use Doctrine\Migrations\Exception\DuplicateMigrationVersion;
10
use Doctrine\Migrations\Exception\MigrationClassNotFound;
11
use Doctrine\Migrations\Exception\MigrationException;
12
use Doctrine\Migrations\Exception\UnknownMigrationVersion;
13
use Doctrine\Migrations\Finder\MigrationFinder;
14
use Doctrine\Migrations\Version\Factory;
15
use Doctrine\Migrations\Version\Version;
16
use const SORT_STRING;
17
use function array_diff;
18
use function array_keys;
19
use function array_map;
20
use function array_search;
21
use function array_unshift;
22
use function class_exists;
23
use function count;
24
use function end;
25
use function get_class;
26
use function implode;
27
use function is_array;
28
use function ksort;
29
use function sprintf;
30
use function substr;
31
32
/**
33
 * The MigrationRepository class is responsible for retrieving migrations, determing what the current migration
34
 * version, etc.
35
 *
36
 * @internal
37
 */
38
class MigrationRepository
39
{
40
    /** @var Configuration */
41
    private $configuration;
42
43
    /** @var Connection */
44
    private $connection;
45
46
    /** @var MigrationFinder */
47
    private $migrationFinder;
48
49
    /** @var Factory */
50
    private $versionFactory;
51
52
    /** @var Version[] */
53
    private $versions = [];
54
55 151
    public function __construct(
56
        Configuration $configuration,
57
        Connection $connection,
58
        MigrationFinder $migrationFinder,
59
        Factory $versionFactory
60
    ) {
61 151
        $this->configuration   = $configuration;
62 151
        $this->connection      = $connection;
63 151
        $this->migrationFinder = $migrationFinder;
64 151
        $this->versionFactory  = $versionFactory;
65 151
    }
66
67
    /**
68
     * @return string[]
69
     */
70 97
    public function findMigrations(string $path) : array
71
    {
72 97
        return $this->migrationFinder->findMigrations(
73 97
            $path,
74 97
            $this->configuration->getMigrationsNamespace()
75
        );
76
    }
77
78
    /** @return Version[] */
79 97
    public function registerMigrationsFromDirectory(string $path) : array
80
    {
81 97
        return $this->registerMigrations($this->findMigrations($path));
82
    }
83
84 74
    public function addVersion(Version $version) : void
85
    {
86 74
        $this->versions[$version->getVersion()] = $version;
87
88 74
        ksort($this->versions, SORT_STRING);
89 74
    }
90
91
    /**
92
     * @param Version[] $versions
93
     */
94 1
    public function addVersions(array $versions) : void
95
    {
96 1
        foreach ($versions as $version) {
97 1
            $this->addVersion($version);
98
        }
99 1
    }
100
101 2
    public function removeMigrationVersionFromDatabase(string $version) : void
102
    {
103 2
        $this->connection->delete(
104 2
            $this->configuration->getMigrationsTableName(),
105 2
            [$this->configuration->getMigrationsColumnName() => $version]
106
        );
107 2
    }
108
109
    /** @throws MigrationException */
110 74
    public function registerMigration(string $version, string $migrationClassName) : Version
111
    {
112 74
        $this->ensureMigrationClassExists($migrationClassName);
113
114 73
        if (isset($this->versions[$version])) {
115 1
            throw DuplicateMigrationVersion::new(
116 1
                $version,
117 1
                get_class($this->versions[$version])
118
            );
119
        }
120
121 73
        $version = $this->versionFactory->createVersion($version, $migrationClassName);
122
123 73
        $this->addVersion($version);
124
125 73
        return $version;
126
    }
127
128
    /**
129
     * @param string[] $migrations
130
     *
131
     * @return Version[]
132
     */
133 101
    public function registerMigrations(array $migrations) : array
134
    {
135 101
        $versions = [];
136
137 101
        foreach ($migrations as $version => $class) {
138 34
            $versions[] = $this->registerMigration((string) $version, $class);
139
        }
140
141 101
        return $versions;
142
    }
143
144 41
    public function getCurrentVersion() : string
145
    {
146 41
        $this->configuration->createMigrationTable();
147
148 41
        if (! $this->configuration->isMigrationTableCreated() && $this->configuration->isDryRun()) {
149 1
            return '0';
150
        }
151
152 41
        $this->configuration->connect();
153
154 41
        $this->loadMigrationsFromDirectory();
155
156 41
        $where = null;
157
158 41
        if (count($this->versions) !== 0) {
159 38
            $migratedVersions = [];
160
161 38
            foreach ($this->versions as $migration) {
162 38
                $migratedVersions[] = sprintf("'%s'", $migration->getVersion());
163
            }
164
165 38
            $where = sprintf(
166 38
                ' WHERE %s IN (%s)',
167 38
                $this->configuration->getQuotedMigrationsColumnName(),
168 38
                implode(', ', $migratedVersions)
169
            );
170
        }
171
172 41
        $sql = sprintf(
173 41
            'SELECT %s FROM %s%s ORDER BY %s DESC',
174 41
            $this->configuration->getQuotedMigrationsColumnName(),
175 41
            $this->configuration->getMigrationsTableName(),
176 41
            $where,
177 41
            $this->configuration->getQuotedMigrationsColumnName()
178
        );
179
180 41
        $sql    = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1);
181 41
        $result = $this->connection->fetchColumn($sql);
182
183 41
        return $result !== false ? (string) $result : '0';
184
    }
185
186
    /**
187
     * @return Version[]
188
     */
189 3
    public function getVersions() : array
190
    {
191 3
        $this->loadMigrationsFromDirectory();
192
193 3
        return $this->versions;
194
    }
195
196 2
    public function clearVersions() : void
197
    {
198 2
        $this->versions = [];
199 2
    }
200
201 23
    public function getVersion(string $version) : Version
202
    {
203 23
        $this->loadMigrationsFromDirectory();
204
205 23
        if (! isset($this->versions[$version])) {
206 2
            throw UnknownMigrationVersion::new($version);
207
        }
208
209 21
        return $this->versions[$version];
210
    }
211
212 20
    public function hasVersion(string $version) : bool
213
    {
214 20
        $this->loadMigrationsFromDirectory();
215
216 20
        return isset($this->versions[$version]);
217
    }
218
219 22
    public function hasVersionMigrated(Version $version) : bool
220
    {
221 22
        return $this->getVersionData($version) !== null;
222
    }
223
224
    /**
225
     * @return mixed[]|null
226
     */
227 26
    public function getVersionData(Version $version) : ?array
228
    {
229 26
        $this->configuration->connect();
230 26
        $this->configuration->createMigrationTable();
231
232 26
        $sql = sprintf(
233 26
            'SELECT %s, %s FROM %s WHERE %s = ?',
234 26
            $this->configuration->getQuotedMigrationsColumnName(),
235 26
            $this->configuration->getQuotedMigrationsExecutedAtColumnName(),
236 26
            $this->configuration->getMigrationsTableName(),
237 26
            $this->configuration->getQuotedMigrationsColumnName()
238
        );
239
240 26
        $data = $this->connection->fetchAssoc($sql, [$version->getVersion()]);
241
242 26
        return is_array($data) ? $data : null;
243
    }
244
245
    /**
246
     * @return Version[]
247
     */
248 38
    public function getMigrations() : array
249
    {
250 38
        $this->loadMigrationsFromDirectory();
251
252 38
        return $this->versions;
253
    }
254
255
    /** @return string[] */
256 15
    public function getAvailableVersions() : array
257
    {
258 15
        $availableVersions = [];
259
260 15
        $this->loadMigrationsFromDirectory();
261
262 15
        foreach ($this->versions as $migration) {
263 14
            $availableVersions[] = $migration->getVersion();
264
        }
265
266 15
        return $availableVersions;
267
    }
268
269
    /** @return string[] */
270 8
    public function getNewVersions() : array
271
    {
272 8
        $availableMigrations = $this->getAvailableVersions();
273 8
        $executedMigrations  = $this->getMigratedVersions();
274
275 8
        return array_diff($availableMigrations, $executedMigrations);
276
    }
277
278
    /** @return string[] */
279 41
    public function getMigratedVersions() : array
280
    {
281 41
        $this->configuration->createMigrationTable();
282
283 41
        if (! $this->configuration->isMigrationTableCreated() && $this->configuration->isDryRun()) {
284 1
            return [];
285
        }
286
287 41
        $this->configuration->connect();
288
289 41
        $sql = sprintf(
290 41
            'SELECT %s FROM %s',
291 41
            $this->configuration->getQuotedMigrationsColumnName(),
292 41
            $this->configuration->getMigrationsTableName()
293
        );
294
295 41
        $result = $this->connection->fetchAll($sql);
296
297 41
        return array_map('current', $result);
298
    }
299
300
    /**
301
     * @return string[]
302
     */
303 8
    public function getExecutedUnavailableMigrations() : array
304
    {
305 8
        $executedMigrations  = $this->getMigratedVersions();
306 8
        $availableMigrations = $this->getAvailableVersions();
307
308 8
        return array_diff($executedMigrations, $availableMigrations);
309
    }
310
311 3
    public function getNumberOfAvailableMigrations() : int
312
    {
313 3
        $this->loadMigrationsFromDirectory();
314
315 3
        return count($this->versions);
316
    }
317
318 25
    public function getLatestVersion() : string
319
    {
320 25
        $this->loadMigrationsFromDirectory();
321
322 25
        $versions = array_keys($this->versions);
323 25
        $latest   = end($versions);
324
325 25
        return $latest !== false ? (string) $latest : '0';
326
    }
327
328 1
    public function getNumberOfExecutedMigrations() : int
329
    {
330 1
        $this->configuration->connect();
331 1
        $this->configuration->createMigrationTable();
332
333 1
        $sql = sprintf(
334 1
            'SELECT COUNT(%s) FROM %s',
335 1
            $this->configuration->getQuotedMigrationsColumnName(),
336 1
            $this->configuration->getMigrationsTableName()
337
        );
338
339 1
        $result = $this->connection->fetchColumn($sql);
340
341 1
        return $result !== false ? (int) $result : 0;
342
    }
343
344 14
    public function getRelativeVersion(string $version, int $delta) : ?string
345
    {
346 14
        $this->loadMigrationsFromDirectory();
347
348 14
        $versions = array_map('strval', array_keys($this->versions));
349
350 14
        array_unshift($versions, '0');
351
352 14
        $offset = array_search($version, $versions, true);
353
354 14
        if ($offset === false) {
355 1
            return null;
356
        }
357
358 14
        $relativeVersion = ((int) $offset) + $delta;
359
360 14
        if (! isset($versions[$relativeVersion])) {
361
            // Unknown version or delta out of bounds.
362 10
            return null;
363
        }
364
365 13
        return $versions[$relativeVersion];
366
    }
367
368 2
    public function getDeltaVersion(string $delta) : ?string
369
    {
370 2
        $symbol = substr($delta, 0, 1);
371 2
        $number = (int) substr($delta, 1);
372
373 2
        if ($number <= 0) {
374 1
            return null;
375
        }
376
377 2
        if ($symbol === '+' || $symbol === '-') {
378 1
            return $this->getRelativeVersion($this->getCurrentVersion(), (int) $delta);
379
        }
380
381 1
        return null;
382
    }
383
384 9
    public function getPrevVersion() : ?string
385
    {
386 9
        return $this->getRelativeVersion($this->getCurrentVersion(), -1);
387
    }
388
389 10
    public function getNextVersion() : ?string
390
    {
391 10
        return $this->getRelativeVersion($this->getCurrentVersion(), 1);
392
    }
393
394 74
    private function loadMigrationsFromDirectory() : void
395
    {
396 74
        $migrationsDirectory = $this->configuration->getMigrationsDirectory();
397
398 74
        if (count($this->versions) !== 0 || $migrationsDirectory === null) {
399 53
            return;
400
        }
401
402 33
        $this->registerMigrationsFromDirectory($migrationsDirectory);
403 33
    }
404
405
    /** @throws MigrationException */
406 74
    private function ensureMigrationClassExists(string $class) : void
407
    {
408 74
        if (! class_exists($class)) {
409 1
            throw MigrationClassNotFound::new(
410 1
                $class,
411 1
                $this->configuration->getMigrationsNamespace()
412
            );
413
        }
414 73
    }
415
}
416