Completed
Push — master ( ff8ed5...dcad7a )
by Mike
15:53 queued 12:44
created

Configuration   F

Complexity

Total Complexity 90

Size/Duplication

Total Lines 810
Duplicated Lines 3.7 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 99.21%

Importance

Changes 9
Bugs 2 Features 2
Metric Value
wmc 90
c 9
b 2
f 2
lcom 1
cbo 11
dl 30
loc 810
ccs 252
cts 254
cp 0.9921
rs 3.9999

45 Methods

Rating   Name   Duplication   Size   Complexity  
A setMigrationsColumnName() 0 4 1
A getMigrationsColumnName() 0 4 1
A registerMigrationsFromDirectory() 0 6 1
A registerMigration() 0 15 2
A ensureMigrationClassExists() 0 6 2
B getCurrentVersion() 0 26 5
A getNumberOfAvailableMigrations() 0 8 2
A getLatestVersion() 0 11 3
B getMigrationsToExecute() 0 27 6
A getAvailableVersions() 0 14 3
A getVersion() 0 12 3
A hasVersion() 0 8 2
A getMigratedVersions() 8 8 1
B createMigrationTable() 0 25 3
A getRelativeVersion() 0 16 4
A __construct() 0 12 3
A areMigrationsOrganizedByYear() 0 4 1
A validate() 0 9 3
A setName() 0 4 1
A getName() 0 4 1
A setOutputWriter() 0 4 1
A getOutputWriter() 0 4 1
A getDateTime() 0 11 2
A getConnection() 0 4 1
A setMigrationsTableName() 0 4 1
A getMigrationsTableName() 0 4 1
A setMigrationsDirectory() 0 4 1
A getMigrationsDirectory() 0 4 1
A setMigrationsNamespace() 0 4 1
A getMigrationsNamespace() 0 4 1
A setMigrationsFinder() 0 13 4
A registerMigrations() 0 9 2
A getMigrations() 0 4 1
A hasVersionMigrated() 0 11 1
A getPrevVersion() 0 4 1
A getNextVersion() 0 4 1
B resolveVersionAlias() 0 20 7
A getNumberOfExecutedMigrations() 8 8 2
A findMigrations() 0 4 1
A setMigrationsAreOrganizedByYear() 0 6 1
A setMigrationsAreOrganizedByYearAndMonth() 0 7 1
A ensureOrganizeMigrationsIsCompatibleWithFinder() 0 9 2
B shouldExecuteMigration() 14 18 5
A areMigrationsOrganizedByYearAndMonth() 0 4 1
A formatVersion() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Configuration often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Configuration, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Migrations\Configuration;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Migrations\Finder\MigrationDeepFinderInterface;
24
use Doctrine\DBAL\Migrations\MigrationException;
25
use Doctrine\DBAL\Migrations\OutputWriter;
26
use Doctrine\DBAL\Migrations\Version;
27
use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface;
28
use Doctrine\DBAL\Migrations\Finder\RecursiveRegexFinder;
29
use Doctrine\DBAL\Schema\Column;
30
use Doctrine\DBAL\Schema\Table;
31
use Doctrine\DBAL\Types\Type;
32
33
/**
34
 * Default Migration Configuration object used for configuring an instance of
35
 * the Migration class. Set the connection, version table name, register migration
36
 * classes/versions, etc.
37
 *
38
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
39
 * @link        www.doctrine-project.org
40
 * @since       2.0
41
 * @author      Jonathan H. Wage <[email protected]>
42
 */
43
class Configuration
44
{
45
    /**
46
     * Configure versions to be organized by year.
47
     */
48
    const VERSIONS_ORGANIZATION_BY_YEAR = 'year';
49
50
    /**
51
     * Configure versions to be organized by year and month.
52
     *
53
     * @var string
54
     */
55
    const VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH = 'year_and_month';
56
57
    /**
58
     * Name of this set of migrations
59
     *
60
     * @var string
61
     */
62
    private $name;
63
64
    /**
65
     * Flag for whether or not the migration table has been created
66
     *
67
     * @var boolean
68
     */
69
    private $migrationTableCreated = false;
70
71
    /**
72
     * Connection instance to use for migrations
73
     *
74
     * @var Connection
75
     */
76
    private $connection;
77
78
    /**
79
     * OutputWriter instance for writing output during migrations
80
     *
81
     * @var OutputWriter
82
     */
83
    private $outputWriter;
84
85
    /**
86
     * The migration finder implementation -- used to load migrations from a
87
     * directory.
88
     *
89
     * @var MigrationFinderInterface
90
     */
91
    private $migrationFinder;
92
93
    /**
94
     * The migration table name to track versions in
95
     *
96
     * @var string
97
     */
98
    private $migrationsTableName = 'doctrine_migration_versions';
99
100
    /**
101
     * The migration column name to track versions in
102
     *
103
     * @var string
104
     */
105
    private $migrationsColumnName = 'version';
106
107
    /**
108
     * The path to a directory where new migration classes will be written
109
     *
110
     * @var string
111
     */
112
    private $migrationsDirectory;
113
114
    /**
115
     * Namespace the migration classes live in
116
     *
117
     * @var string
118
     */
119
    private $migrationsNamespace;
120
121
    /**
122
     * Array of the registered migrations
123
     *
124
     * @var Version[]
125
     */
126
    private $migrations = [];
127
128
    /**
129
     * Versions are organized by year.
130
     *
131
     * @var boolean
132
     */
133
    private $migrationsAreOrganizedByYear = false;
134
135
    /**
136
     * Versions are organized by year and month.
137
     *
138
     * @var boolean
139
     */
140
    private $migrationsAreOrganizedByYearAndMonth = false;
141
142
    /**
143
     * Construct a migration configuration object.
144
     *
145
     * @param Connection               $connection   A Connection instance
146
     * @param OutputWriter             $outputWriter A OutputWriter instance
147
     * @param MigrationFinderInterface $finder       Migration files finder
148
     */
149 254
    public function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null)
150
    {
151 254
        $this->connection = $connection;
152 254
        if ($outputWriter === null) {
153 221
            $outputWriter = new OutputWriter();
154 221
        }
155 254
        $this->outputWriter = $outputWriter;
156 254
        if ($finder === null) {
157 250
            $finder = new RecursiveRegexFinder();
158 250
        }
159 254
        $this->migrationFinder = $finder;
160 254
    }
161
162
    /**
163
     * @return bool
164
     */
165 22
    public function areMigrationsOrganizedByYear()
166
    {
167 22
        return $this->migrationsAreOrganizedByYear;
168
    }
169
170
    /**
171
     * @return bool
172
     */
173 22
    public function areMigrationsOrganizedByYearAndMonth()
174
    {
175 22
        return $this->migrationsAreOrganizedByYearAndMonth;
176
    }
177
178
    /**
179
     * Validation that this instance has all the required properties configured
180
     *
181
     * @throws MigrationException
182
     */
183 137
    public function validate()
184
    {
185 137
        if (!$this->migrationsNamespace) {
186 10
            throw MigrationException::migrationsNamespaceRequired();
187
        }
188 127
        if (!$this->migrationsDirectory) {
189 10
            throw MigrationException::migrationsDirectoryRequired();
190
        }
191 117
    }
192
193
    /**
194
     * Set the name of this set of migrations
195
     *
196
     * @param string $name The name of this set of migrations
197
     */
198 63
    public function setName($name)
199
    {
200 63
        $this->name = $name;
201 63
    }
202
203
    /**
204
     * Returns the name of this set of migrations
205
     *
206
     * @return string $name The name of this set of migrations
207
     */
208 15
    public function getName()
209
    {
210 15
        return $this->name;
211
    }
212
213
    /**
214
     * Sets the output writer.
215
     *
216
     * @param OutputWriter $outputWriter
217
     */
218 1
    public function setOutputWriter(OutputWriter $outputWriter)
219
    {
220 1
        $this->outputWriter = $outputWriter;
221 1
    }
222
223
    /**
224
     * Returns the OutputWriter instance
225
     *
226
     * @return OutputWriter $outputWriter  The OutputWriter instance
227
     */
228 103
    public function getOutputWriter()
229
    {
230 103
        return $this->outputWriter;
231
    }
232
233
    /**
234
     * Returns a timestamp version as a formatted date
235
     *
236
     * @param string $version
237
     *
238
     * @return string The formatted version
239
     * @deprecated
240
     */
241 14
    public function formatVersion($version)
242
    {
243 14
        return $this->getDateTime($version);
244
    }
245
246
    /**
247
     * Returns the datetime of a migration
248
     *
249
     * @param $version
250
     * @return string
251
     */
252 21
    public function getDateTime($version)
253
    {
254 21
        $datetime = str_replace('Version', '', $version);
255 21
        $datetime = \DateTime::createFromFormat('YmdHis', $datetime);
256
257 21
        if ($datetime === false) {
258 7
            return '';
259
        }
260
261 14
        return $datetime->format('Y-m-d H:i:s');
262
    }
263
264
    /**
265
     * Returns the Connection instance
266
     *
267
     * @return Connection $connection  The Connection instance
268
     */
269 103
    public function getConnection()
270
    {
271 103
        return $this->connection;
272
    }
273
274
    /**
275
     * Set the migration table name
276
     *
277
     * @param string $tableName The migration table name
278
     */
279 82
    public function setMigrationsTableName($tableName)
280
    {
281 82
        $this->migrationsTableName = $tableName;
282 82
    }
283
284
    /**
285
     * Returns the migration table name
286
     *
287
     * @return string $migrationsTableName The migration table name
288
     */
289 67
    public function getMigrationsTableName()
290
    {
291 67
        return $this->migrationsTableName;
292
    }
293
294
    /**
295
     * Set the migration column name
296
     *
297
     * @param string $columnName The migration column name
298
     */
299 53
    public function setMigrationsColumnName($columnName)
300
    {
301 53
        $this->migrationsColumnName = $columnName;
302 53
    }
303
304
    /**
305
     * Returns the migration column name
306
     *
307
     * @return string $migrationsColumnName The migration column name
308
     */
309 62
    public function getMigrationsColumnName()
310
    {
311 62
        return $this->migrationsColumnName;
312
    }
313
314
    /**
315
     * Set the new migrations directory where new migration classes are generated
316
     *
317
     * @param string $migrationsDirectory The new migrations directory
318
     */
319 173
    public function setMigrationsDirectory($migrationsDirectory)
320
    {
321 173
        $this->migrationsDirectory = $migrationsDirectory;
322 173
    }
323
324
    /**
325
     * Returns the new migrations directory where new migration classes are generated
326
     *
327
     * @return string $migrationsDirectory The new migrations directory
328
     */
329 64
    public function getMigrationsDirectory()
330
    {
331 64
        return $this->migrationsDirectory;
332
    }
333
334
    /**
335
     * Set the migrations namespace
336
     *
337
     * @param string $migrationsNamespace The migrations namespace
338
     */
339 185
    public function setMigrationsNamespace($migrationsNamespace)
340
    {
341 185
        $this->migrationsNamespace = $migrationsNamespace;
342 185
    }
343
344
    /**
345
     * Returns the migrations namespace
346
     *
347
     * @return string $migrationsNamespace The migrations namespace
348
     */
349 91
    public function getMigrationsNamespace()
350
    {
351 91
        return $this->migrationsNamespace;
352
    }
353
354
    /**
355
     * Set the implementation of the migration finder.
356
     *
357
     * @param MigrationFinderInterface $finder The new migration finder
358
     * @throws MigrationException
359
     */
360 8
    public function setMigrationsFinder(MigrationFinderInterface $finder)
361
    {
362 8
        if (($this->migrationsAreOrganizedByYear || $this->migrationsAreOrganizedByYearAndMonth) &&
363 8
            !($finder instanceof MigrationDeepFinderInterface)) {
364
365 4
            throw MigrationException::configurationIncompatibleWithFinder(
366 4
                'organize-migrations',
367
                $finder
368 4
            );
369
        }
370
371 4
        $this->migrationFinder = $finder;
372 4
    }
373
374
    /**
375
     * Register migrations from a given directory. Recursively finds all files
376
     * with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers
377
     * them as migrations.
378
     *
379
     * @param string $path The root directory to where some migration classes live.
380
     *
381
     * @return Version[] The array of migrations registered.
382
     */
383 103
    public function registerMigrationsFromDirectory($path)
384
    {
385 103
        $this->validate();
386
387 87
        return $this->registerMigrations($this->findMigrations($path));
388
    }
389
390
    /**
391
     * Register a single migration version to be executed by a AbstractMigration
392
     * class.
393
     *
394
     * @param string $version The version of the migration in the format YYYYMMDDHHMMSS.
395
     * @param string $class   The migration class to execute for the version.
396
     *
397
     * @return Version
398
     *
399
     * @throws MigrationException
400
     */
401 61
    public function registerMigration($version, $class)
402
    {
403 61
        $this->ensureMigrationClassExists($class);
404
405 60
        $version = (string) $version;
406 60
        $class = (string) $class;
407 60
        if (isset($this->migrations[$version])) {
408 1
            throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version]));
409
        }
410 60
        $version = new Version($this, $version, $class);
411 60
        $this->migrations[$version->getVersion()] = $version;
412 60
        ksort($this->migrations, SORT_STRING);
413
414 60
        return $version;
415
    }
416
417
    /**
418
     * Register an array of migrations. Each key of the array is the version and
419
     * the value is the migration class name.
420
     *
421
     *
422
     * @param array $migrations
423
     *
424
     * @return Version[]
425
     */
426 90
    public function registerMigrations(array $migrations)
427
    {
428 90
        $versions = [];
429 90
        foreach ($migrations as $version => $class) {
430 33
            $versions[] = $this->registerMigration($version, $class);
431 89
        }
432
433 89
        return $versions;
434
    }
435
436
    /**
437
     * Get the array of registered migration versions.
438
     *
439
     * @return Version[] $migrations
440
     */
441 28
    public function getMigrations()
442
    {
443 28
        return $this->migrations;
444
    }
445
446
    /**
447
     * Returns the Version instance for a given version in the format YYYYMMDDHHMMSS.
448
     *
449
     * @param string $version The version string in the format YYYYMMDDHHMMSS.
450
     *
451
     * @return Version
452
     *
453
     * @throws MigrationException Throws exception if migration version does not exist.
454
     */
455 18
    public function getVersion($version)
456
    {
457 18
        if (empty($this->migrations)) {
458 7
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
459 7
        }
460
461 18
        if (!isset($this->migrations[$version])) {
462 2
            throw MigrationException::unknownMigrationVersion($version);
463
        }
464
465 16
        return $this->migrations[$version];
466
    }
467
468
    /**
469
     * Check if a version exists.
470
     *
471
     * @param string $version
472
     *
473
     * @return boolean
474
     */
475 21
    public function hasVersion($version)
476
    {
477 21
        if (empty($this->migrations)) {
478 4
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
479 2
        }
480
481 19
        return isset($this->migrations[$version]);
482
    }
483
484
    /**
485
     * Check if a version has been migrated or not yet
486
     *
487
     * @param Version $version
488
     *
489
     * @return boolean
490
     */
491 21
    public function hasVersionMigrated(Version $version)
492
    {
493 21
        $this->createMigrationTable();
494
495 21
        $version = $this->connection->fetchColumn(
496 21
            "SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName . " WHERE " . $this->migrationsColumnName . " = ?",
497 21
            [$version->getVersion()]
498 21
        );
499
500 21
        return $version !== false;
501
    }
502
503
    /**
504
     * Returns all migrated versions from the versions table, in an array.
505
     *
506
     * @return Version[]
507
     */
508 33 View Code Duplication
    public function getMigratedVersions()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
509
    {
510 33
        $this->createMigrationTable();
511
512 33
        $ret = $this->connection->fetchAll("SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName);
513
514 33
        return array_map('current', $ret);
515
    }
516
517
    /**
518
     * Returns an array of available migration version numbers.
519
     *
520
     * @return array
521
     */
522 15
    public function getAvailableVersions()
523
    {
524 15
        $availableVersions = [];
525
526 15
        if (empty($this->migrations)) {
527 3
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
528 1
        }
529
530 13
        foreach ($this->migrations as $migration) {
531 13
            $availableVersions[] = $migration->getVersion();
532 13
        }
533
534 13
        return $availableVersions;
535
    }
536
537
    /**
538
     * Returns the current migrated version from the versions table.
539
     *
540
     * @return string
541
     */
542 35
    public function getCurrentVersion()
543
    {
544 35
        $this->createMigrationTable();
545
546 33
        if (empty($this->migrations)) {
547 15
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
548 15
        }
549
550 33
        $where = null;
551 33
        if (!empty($this->migrations)) {
552 29
            $migratedVersions = [];
553 29
            foreach ($this->migrations as $migration) {
554 29
                $migratedVersions[] = sprintf("'%s'", $migration->getVersion());
555 29
            }
556 29
            $where = " WHERE " . $this->migrationsColumnName . " IN (" . implode(', ', $migratedVersions) . ")";
557 29
        }
558
559 33
        $sql = sprintf("SELECT %s FROM %s%s ORDER BY %s DESC",
560 33
            $this->migrationsColumnName, $this->migrationsTableName, $where, $this->migrationsColumnName
561 33
        );
562
563 33
        $sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1);
564 33
        $result = $this->connection->fetchColumn($sql);
565
566 33
        return $result !== false ? (string) $result : '0';
567
    }
568
569
    /**
570
     * Returns the version prior to the current version.
571
     *
572
     * @return string|null A version string, or null if the current version is
573
     *                     the first.
574
     */
575 9
    public function getPrevVersion()
576
    {
577 9
        return $this->getRelativeVersion($this->getCurrentVersion(), -1);
578
    }
579
580
    /**
581
     * Returns the version following the current version.
582
     *
583
     * @return string|null A version string, or null if the current version is
584
     *                     the latest.
585
     */
586 9
    public function getNextVersion()
587
    {
588 9
        return $this->getRelativeVersion($this->getCurrentVersion(), 1);
589
    }
590
591
    /**
592
     * Returns the version with the specified offset to the specified version.
593
     *
594
     * @return string|null A version string, or null if the specified version
595
     *                     is unknown or the specified delta is not within the
596
     *                     list of available versions.
597
     */
598 14
    public function getRelativeVersion($version, $delta)
599
    {
600 14
        if (empty($this->migrations)) {
601 5
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
602 3
        }
603
604 12
        $versions = array_map('strval', array_keys($this->migrations));
605 12
        array_unshift($versions, '0');
606 12
        $offset = array_search((string)$version, $versions);
607 12
        if ($offset === false || !isset($versions[$offset + $delta])) {
608
            // Unknown version or delta out of bounds.
609 10
            return null;
610
        }
611
612 10
        return $versions[$offset + $delta];
613
    }
614
615
    /**
616
     * Returns the version number from an alias.
617
     *
618
     * Supported aliases are:
619
     * - first: The very first version before any migrations have been run.
620
     * - current: The current version.
621
     * - prev: The version prior to the current version.
622
     * - next: The version following the current version.
623
     * - latest: The latest available version.
624
     *
625
     * If an existing version number is specified, it is returned verbatimly.
626
     *
627
     * @return string|null A version number, or null if the specified alias
628
     *                     does not map to an existing version, e.g. if "next"
629
     *                     is passed but the current version is already the
630
     *                     latest.
631
     */
632 8
    public function resolveVersionAlias($alias)
633
    {
634 8
        if ($this->hasVersion($alias)) {
635 1
            return $alias;
636
        }
637
        switch ($alias) {
638 8
            case 'first':
639 1
                return '0';
640 8
            case 'current':
641 8
                return $this->getCurrentVersion();
642 8
            case 'prev':
643 8
                return $this->getPrevVersion();
644 8
            case 'next':
645 8
                return $this->getNextVersion();
646 8
            case 'latest':
647 8
                return $this->getLatestVersion();
648 1
            default:
649 1
                return null;
650 1
        }
651
    }
652
653
    /**
654
     * Returns the total number of executed migration versions
655
     *
656
     * @return integer
657
     */
658 1 View Code Duplication
    public function getNumberOfExecutedMigrations()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
659
    {
660 1
        $this->createMigrationTable();
661
662 1
        $result = $this->connection->fetchColumn("SELECT COUNT(" . $this->migrationsColumnName . ") FROM " . $this->migrationsTableName);
663
664 1
        return $result !== false ? $result : 0;
665
    }
666
667
    /**
668
     * Returns the total number of available migration versions
669
     *
670
     * @return integer
671
     */
672 5
    public function getNumberOfAvailableMigrations()
673
    {
674 5
        if (empty($this->migrations)) {
675 4
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
676 2
        }
677
678 3
        return count($this->migrations);
679
    }
680
681
    /**
682
     * Returns the latest available migration version.
683
     *
684
     * @return string The version string in the format YYYYMMDDHHMMSS.
685
     */
686 21
    public function getLatestVersion()
687
    {
688 21
        if (empty($this->migrations)) {
689 7
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
690 5
        }
691
692 19
        $versions = array_keys($this->migrations);
693 19
        $latest = end($versions);
694
695 19
        return $latest !== false ? (string) $latest : '0';
696
    }
697
698
    /**
699
     * Create the migration table to track migrations with.
700
     *
701
     * @return boolean Whether or not the table was created.
702
     */
703 55
    public function createMigrationTable()
704
    {
705 55
        $this->validate();
706
707 53
        if ($this->migrationTableCreated) {
708 43
            return false;
709
        }
710
711 53
        if ($this->connection->getSchemaManager()->tablesExist([$this->migrationsTableName])) {
712 3
            $this->migrationTableCreated = true;
713
714 3
            return false;
715
        }
716
717
        $columns = [
718 53
            $this->migrationsColumnName => new Column($this->migrationsColumnName, Type::getType('string'), ['length' => 255]),
719 53
        ];
720 53
        $table = new Table($this->migrationsTableName, $columns);
721 53
        $table->setPrimaryKey([$this->migrationsColumnName]);
722 53
        $this->connection->getSchemaManager()->createTable($table);
723
724 53
        $this->migrationTableCreated = true;
725
726 53
        return true;
727
    }
728
729
    /**
730
     * Returns the array of migrations to executed based on the given direction
731
     * and target version number.
732
     *
733
     * @param string $direction The direction we are migrating.
734
     * @param string $to        The version to migrate to.
735
     *
736
     * @return Version[] $migrations   The array of migrations we can execute.
737
     */
738 33
    public function getMigrationsToExecute($direction, $to)
739
    {
740 33
        if (empty($this->migrations)) {
741 11
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
742 5
        }
743
744 27
        if ($direction === Version::DIRECTION_DOWN) {
745 7
            if (count($this->migrations)) {
746 7
                $allVersions = array_reverse(array_keys($this->migrations));
747 7
                $classes = array_reverse(array_values($this->migrations));
748 7
                $allVersions = array_combine($allVersions, $classes);
749 7
            } else {
750
                $allVersions = [];
751
            }
752 7
        } else {
753 25
            $allVersions = $this->migrations;
754
        }
755 27
        $versions = [];
756 27
        $migrated = $this->getMigratedVersions();
757 27
        foreach ($allVersions as $version) {
758 25
            if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) {
759 23
                $versions[$version->getVersion()] = $version;
760 23
            }
761 27
        }
762
763 27
        return $versions;
764
    }
765
766
    /**
767
     * Find all the migrations in a given directory.
768
     *
769
     * @param   string $path the directory to search.
770
     * @return  array
771
     */
772 87
    protected function findMigrations($path)
773
    {
774 87
        return $this->migrationFinder->findMigrations($path, $this->getMigrationsNamespace());
775
    }
776
777
    /**
778
     * @param bool $migrationsAreOrganizedByYear
779
     * @throws MigrationException
780
     */
781 9
    public function setMigrationsAreOrganizedByYear($migrationsAreOrganizedByYear = true)
782
    {
783 9
        $this->ensureOrganizeMigrationsIsCompatibleWithFinder();
784
785 5
        $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYear;
786 5
    }
787
788
    /**
789
     * @param bool $migrationsAreOrganizedByYearAndMonth
790
     * @throws MigrationException
791
     */
792 10
    public function setMigrationsAreOrganizedByYearAndMonth($migrationsAreOrganizedByYearAndMonth = true)
793
    {
794 10
        $this->ensureOrganizeMigrationsIsCompatibleWithFinder();
795
796 10
        $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYearAndMonth;
797 10
        $this->migrationsAreOrganizedByYearAndMonth = $migrationsAreOrganizedByYearAndMonth;
798 10
    }
799
800
    /**
801
     * @throws MigrationException
802
     */
803 19
    private function ensureOrganizeMigrationsIsCompatibleWithFinder()
804
    {
805 19
        if (!($this->migrationFinder instanceof MigrationDeepFinderInterface)) {
806 4
            throw MigrationException::configurationIncompatibleWithFinder(
807 4
                'organize-migrations',
808 4
                $this->migrationFinder
809 4
            );
810
        }
811 15
    }
812
813
    /**
814
     * Check if we should execute a migration for a given direction and target
815
     * migration version.
816
     *
817
     * @param string  $direction The direction we are migrating.
818
     * @param Version $version   The Version instance to check.
819
     * @param string  $to        The version we are migrating to.
820
     * @param array   $migrated  Migrated versions array.
821
     *
822
     * @return boolean
823
     */
824 25
    private function shouldExecuteMigration($direction, Version $version, $to, $migrated)
825
    {
826 25 View Code Duplication
        if ($direction === Version::DIRECTION_DOWN) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
827 7
            if (!in_array($version->getVersion(), $migrated)) {
828 4
                return false;
829
            }
830
831 5
            return $version->getVersion() > $to;
832
        }
833
834 23 View Code Duplication
        if ($direction === Version::DIRECTION_UP) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
835 23
            if (in_array($version->getVersion(), $migrated)) {
836 7
                return false;
837
            }
838
839 23
            return $version->getVersion() <= $to;
840
        }
841
    }
842
843
    /**
844
     * @param string $class
845
     */
846 61
    private function ensureMigrationClassExists($class)
847
    {
848 61
        if ( ! class_exists($class)) {
849 1
            throw MigrationException::migrationClassNotFound($class, $this->getMigrationsNamespace());
850
        }
851 60
    }
852
}
853