Completed
Push — develop ( 0438cb...146147 )
by Neomerx
04:10 queued 02:30
created

BaseSeedRunner::readSeeded()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 14
cp 0
rs 9.3142
c 0
b 0
f 0
cc 3
eloc 15
nc 2
nop 1
crap 12
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\SeedInterface;
27
use Psr\Container\ContainerInterface;
28
29
/**
30
 * @package Limoncello\Application
31
 */
32
abstract class BaseSeedRunner
33
{
34
    /** Seed column name */
35
    const SEEDS_COLUMN_ID = 'id';
36
37
    /** Seed column name */
38
    const SEEDS_COLUMN_CLASS = 'class';
39
40
    /** Seed column name */
41
    const SEEDS_COLUMN_SEEDED_AT = 'seeded_at';
42
43
    /**
44
     * @var string
45
     */
46
    private $seedsTable;
47
48
    /**
49
     * @var null|callable
50
     */
51
    private $seedInit = null;
52
53
    /**
54
     * @var IoInterface
55
     */
56
    private $inOut;
57
58
    /**
59
     * @return string[]
60
     */
61
    abstract protected function getSeedClasses(): array;
62
63
    /**
64
     * @param IoInterface $inOut
65
     * @param callable    $seedInit
0 ignored issues
show
Documentation introduced by
Should the type for parameter $seedInit not be null|callable? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
66
     * @param string      $seedsTable
67
     */
68 2
    public function __construct(
69
        IoInterface $inOut,
70
        callable $seedInit = null,
71
        string $seedsTable = BaseMigrationRunner::SEEDS_TABLE
72
    ) {
73 2
        assert(empty($seedsTable) === false);
74
75 2
        $this->seedInit    = $seedInit;
76 2
        $this->seedsTable  = $seedsTable;
77
78 2
        $this->setIO($inOut);
79
    }
80
81
    /**
82
     * @param ContainerInterface $container
83
     *
84
     * @return void
85
     */
86
    public function run(ContainerInterface $container): void
87
    {
88
        foreach ($this->getSeeds($container) as $seederClass) {
89
            $this->getIO()->writeInfo("Starting seed for `$seederClass`...", 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...
90
            $this->executeSeedInit($container, $seederClass);
91
            /** @var SeedInterface $seeder */
92
            $seeder = new $seederClass();
93
            $seeder->init($container)->run();
94
            $this->getIO()->writeInfo("Seed finished for `$seederClass`.", 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...
95
        }
96
    }
97
98
    /**
99
     * @param ContainerInterface $container
100
     *
101
     * @return Generator
102
     *
103
     * @SuppressWarnings(PHPMD.ElseExpression)
104
     */
105 View Code Duplication
    protected function getSeeds(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...
106
    {
107
        $connection = $this->getConnection($container);
108
        $manager    = $connection->getSchemaManager();
109
110
        if ($manager->tablesExist([$this->getSeedsTable()]) === true) {
111
            $seeded = $this->readSeeded($connection);
112
        } else {
113
            $this->createSeedsTable($manager);
114
            $seeded = [];
115
        }
116
117
        $notYetSeeded = array_diff($this->getSeedClasses(), $seeded);
118
119
        foreach ($notYetSeeded as $class) {
120
            yield $class;
121
            $this->saveSeed($connection, $class);
122
        }
123
    }
124
125
    /**
126
     * @param ContainerInterface $container
127
     *
128
     * @return Connection
129
     */
130
    protected function getConnection(ContainerInterface $container): Connection
131
    {
132
        return $container->get(Connection::class);
133
    }
134
135
    /**
136
     * @param ContainerInterface $container
137
     * @param string             $seedClass
138
     *
139
     * @return void
140
     */
141
    protected function executeSeedInit(ContainerInterface $container, string $seedClass): void
142
    {
143
        if ($this->seedInit !== null) {
144
            call_user_func($this->seedInit, $container, $seedClass);
145
        }
146
    }
147
148
    /**
149
     * @return IoInterface
150
     */
151
    protected function getIO(): IoInterface
152
    {
153
        return $this->inOut;
154
    }
155
156
    /**
157
     * @param IoInterface $inOut
158
     *
159
     * @return self
160
     */
161 2
    private function setIO(IoInterface $inOut): self
162
    {
163 2
        $this->inOut = $inOut;
164
165 2
        return $this;
166
    }
167
168
    /**
169
     * @param AbstractSchemaManager $manager
170
     *
171
     * @return void
172
     */
173 View Code Duplication
    private function createSeedsTable(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...
174
    {
175
        $table = new Table($this->getSeedsTable());
176
177
        $table
178
            ->addColumn(static::SEEDS_COLUMN_ID, Type::INTEGER)
179
            ->setUnsigned(true)
180
            ->setAutoincrement(true);
181
        $table
182
            ->addColumn(static::SEEDS_COLUMN_CLASS, Type::STRING)
183
            ->setLength(255);
184
        $table
185
            ->addColumn(static::SEEDS_COLUMN_SEEDED_AT, Type::DATETIME);
186
187
        $table->setPrimaryKey([static::SEEDS_COLUMN_ID]);
188
        $table->addUniqueIndex([static::SEEDS_COLUMN_CLASS]);
189
190
        $manager->createTable($table);
191
    }
192
193
    /**
194
     * @param Connection $connection
195
     *
196
     * @return array
197
     */
198
    private function readSeeded(Connection $connection): array
199
    {
200
        $builder = $connection->createQueryBuilder();
201
        $seeded  = [];
202
203
        if ($connection->getSchemaManager()->tablesExist([$this->getSeedsTable()]) === true) {
204
            $seeds = $builder
205
                ->select(static::SEEDS_COLUMN_ID, static::SEEDS_COLUMN_CLASS)
206
                ->from($this->getSeedsTable())
207
                ->orderBy(static::SEEDS_COLUMN_ID)
208
                ->execute()
209
                ->fetchAll();
210
            foreach ($seeds as $seed) {
211
                $index          = $seed[static::SEEDS_COLUMN_ID];
212
                $class          = $seed[static::SEEDS_COLUMN_CLASS];
213
                $seeded[$index] = $class;
214
            }
215
        }
216
217
        return $seeded;
218
    }
219
220
    /**
221
     * @param Connection $connection
222
     * @param string     $class
223
     *
224
     * @return void
225
     */
226 View Code Duplication
    private function saveSeed(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...
227
    {
228
        $format = $connection->getSchemaManager()->getDatabasePlatform()->getDateTimeFormatString();
229
        $now    = (new DateTimeImmutable())->format($format);
230
        $connection->insert($this->getSeedsTable(), [
231
            static::SEEDS_COLUMN_CLASS     => $class,
232
            static::SEEDS_COLUMN_SEEDED_AT => $now,
233
        ]);
234
    }
235
236
    /**
237
     * @return string
238
     */
239
    private function getSeedsTable(): string
240
    {
241
        return $this->seedsTable;
242
    }
243
}
244