Completed
Push — master ( 146147...1776d4 )
by Neomerx
01:58
created

BaseMigrationRunner::createMigrationsTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 14

Duplication

Lines 19
Ratio 100 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
dl 19
loc 19
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 14
nc 1
nop 1
crap 1
1
<?php namespace Limoncello\Application\Data;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use DateTimeImmutable;
20
use Doctrine\DBAL\Connection;
21
use Doctrine\DBAL\Schema\AbstractSchemaManager;
22
use Doctrine\DBAL\Schema\Table;
23
use Doctrine\DBAL\Types\Type;
24
use Generator;
25
use Limoncello\Contracts\Commands\IoInterface;
26
use Limoncello\Contracts\Data\MigrationInterface;
27
use Psr\Container\ContainerInterface;
28
29
/**
30
 * @package Limoncello\Application
31
 */
32
abstract class BaseMigrationRunner
33
{
34
    /** Migrations table name */
35
    const MIGRATIONS_TABLE = '_migrations';
36
37
    /** Migration column name */
38
    const MIGRATIONS_COLUMN_ID = 'id';
39
40
    /** Migration column name */
41
    const MIGRATIONS_COLUMN_CLASS = 'class';
42
43
    /** Migration column name */
44
    const MIGRATIONS_COLUMN_MIGRATED_AT = 'migrated_at';
45
46
    /** Seeds table name */
47
    const SEEDS_TABLE = '_seeds';
48
49
    /**
50
     * @var IoInterface
51
     */
52
    private $inOut;
53
54
    /**
55
     * @return string[]
56
     */
57
    abstract protected function getMigrationClasses(): array;
58
59
    /**
60
     * @param IoInterface $inOut
61
     */
62 2
    public function __construct(IoInterface $inOut)
63
    {
64 2
        $this->setIO($inOut);
65
    }
66
67
    /**
68
     * @param ContainerInterface $container
69
     *
70
     * @return void
71
     */
72 1
    public function migrate(ContainerInterface $container): void
73
    {
74 1 View Code Duplication
        foreach ($this->getMigrations($container) as $class) {
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...
75 1
            assert(is_string($class));
76 1
            $this->getIO()->writeInfo("Starting migration for `$class`..." . PHP_EOL, IoInterface::VERBOSITY_VERBOSE);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...face::VERBOSITY_VERBOSE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
77
            /** @var MigrationInterface $migration */
78 1
            $migration = new $class($container);
79 1
            $migration->init($container)->migrate();
80 1
            $this->getIO()->writeInfo("Migration finished for `$class`." . PHP_EOL, IoInterface::VERBOSITY_NORMAL);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...rface::VERBOSITY_NORMAL.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
81
        }
82
    }
83
84
    /**
85
     * @param ContainerInterface $container
86
     *
87
     * @return void
88
     */
89 1
    public function rollback(ContainerInterface $container): void
90
    {
91 1 View Code Duplication
        foreach ($this->getRollbacks($container) as $class) {
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...
92 1
            assert(is_string($class));
93 1
            $this->getIO()->writeInfo("Starting rollback for `$class`..." . PHP_EOL, IoInterface::VERBOSITY_VERBOSE);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...face::VERBOSITY_VERBOSE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
94
            /** @var MigrationInterface $migration */
95 1
            $migration = new $class($container);
96 1
            $migration->init($container)->rollback();
97 1
            $this->getIO()->writeInfo("Rollback finished for `$class`." . PHP_EOL, IoInterface::VERBOSITY_NORMAL);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...rface::VERBOSITY_NORMAL.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
98
        }
99
100 1
        $manager = $this->getConnection($container)->getSchemaManager();
101 1
        if ($manager->tablesExist([static::MIGRATIONS_TABLE]) === true) {
102 1
            $manager->dropTable(static::MIGRATIONS_TABLE);
103
        }
104 1
        if ($manager->tablesExist([static::SEEDS_TABLE]) === true) {
105 1
            $manager->dropTable(static::SEEDS_TABLE);
106
        }
107
    }
108
109
    /**
110
     * @return IoInterface
111
     */
112 1
    protected function getIO(): IoInterface
113
    {
114 1
        return $this->inOut;
115
    }
116
117
    /**
118
     * @param IoInterface $inOut
119
     *
120
     * @return self
121
     */
122 2
    private function setIO(IoInterface $inOut): self
123
    {
124 2
        $this->inOut = $inOut;
125
126 2
        return $this;
127
    }
128
129
    /**
130
     * @param ContainerInterface $container
131
     *
132
     * @return Generator
133
     *
134
     * @SuppressWarnings(PHPMD.ElseExpression)
135
     */
136 1 View Code Duplication
    private function getMigrations(ContainerInterface $container): Generator
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...
137
    {
138 1
        $connection = $this->getConnection($container);
139 1
        $manager    = $connection->getSchemaManager();
140
141 1
        if ($manager->tablesExist([static::MIGRATIONS_TABLE]) === true) {
142 1
            $migrated = $this->readMigrated($connection);
143
        } else {
144 1
            $this->createMigrationsTable($manager);
145 1
            $migrated = [];
146
        }
147
148 1
        $notYetMigrated = array_diff($this->getMigrationClasses(), $migrated);
149
150 1
        foreach ($notYetMigrated as $class) {
151 1
            yield $class;
152 1
            $this->saveMigration($connection, $class);
153
        }
154
    }
155
156
    /**
157
     * @param ContainerInterface $container
158
     *
159
     * @return Generator
160
     */
161 1
    private function getRollbacks(ContainerInterface $container): Generator
162
    {
163 1
        $connection = $this->getConnection($container);
164 1
        $migrated   = $this->readMigrated($connection);
165
166 1
        foreach (array_reverse($migrated, true) as $index => $class) {
167 1
            yield $class;
168 1
            $this->removeMigration($connection, $index);
169
        }
170
    }
171
172
    /**
173
     * @param ContainerInterface $container
174
     *
175
     * @return Connection
176
     */
177 1
    private function getConnection(ContainerInterface $container): Connection
178
    {
179 1
        return $container->get(Connection::class);
180
    }
181
182
    /**
183
     * @param AbstractSchemaManager $manager
184
     *
185
     * @return void
186
     */
187 1 View Code Duplication
    private function createMigrationsTable(AbstractSchemaManager $manager): void
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...
188
    {
189 1
        $table = new Table(static::MIGRATIONS_TABLE);
190
191
        $table
192 1
            ->addColumn(static::MIGRATIONS_COLUMN_ID, Type::INTEGER)
193 1
            ->setUnsigned(true)
194 1
            ->setAutoincrement(true);
195
        $table
196 1
            ->addColumn(static::MIGRATIONS_COLUMN_CLASS, Type::STRING)
197 1
            ->setLength(255);
198
        $table
199 1
            ->addColumn(static::MIGRATIONS_COLUMN_MIGRATED_AT, Type::DATETIME);
200
201 1
        $table->setPrimaryKey([static::MIGRATIONS_COLUMN_ID]);
202 1
        $table->addUniqueIndex([static::MIGRATIONS_COLUMN_CLASS]);
203
204 1
        $manager->createTable($table);
205
    }
206
207
    /**
208
     * @param Connection $connection
209
     *
210
     * @return array
211
     */
212 1
    private function readMigrated(Connection $connection): array
213
    {
214 1
        $builder  = $connection->createQueryBuilder();
215 1
        $migrated = [];
216
217 1
        if ($connection->getSchemaManager()->tablesExist([static::MIGRATIONS_TABLE]) === true) {
218
            $migrations = $builder
219 1
                ->select(static::MIGRATIONS_COLUMN_ID, static::MIGRATIONS_COLUMN_CLASS)
220 1
                ->from(static::MIGRATIONS_TABLE)
221 1
                ->orderBy(static::MIGRATIONS_COLUMN_ID)
222 1
                ->execute()
223 1
                ->fetchAll();
224 1
            foreach ($migrations as $migration) {
225 1
                $index            = $migration[static::MIGRATIONS_COLUMN_ID];
226 1
                $class            = $migration[static::MIGRATIONS_COLUMN_CLASS];
227 1
                $migrated[$index] = $class;
228
            }
229
        }
230
231 1
        return $migrated;
232
    }
233
234
    /**
235
     * @param Connection $connection
236
     * @param string     $class
237
     *
238
     * @return void
239
     */
240 1 View Code Duplication
    private function saveMigration(Connection $connection, string $class): void
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...
241
    {
242 1
        $format = $connection->getSchemaManager()->getDatabasePlatform()->getDateTimeFormatString();
243 1
        $now    = (new DateTimeImmutable())->format($format);
244 1
        $connection->insert(static::MIGRATIONS_TABLE, [
245 1
            static::MIGRATIONS_COLUMN_CLASS       => $class,
246 1
            static::MIGRATIONS_COLUMN_MIGRATED_AT => $now,
247
        ]);
248
    }
249
250
    /**
251
     * @param Connection $connection
252
     * @param int        $index
253
     *
254
     * @return void
255
     */
256 1
    private function removeMigration(Connection $connection, int $index): void
257
    {
258 1
        $connection->delete(static::MIGRATIONS_TABLE, [static::MIGRATIONS_COLUMN_ID => $index]);
259
    }
260
}
261