Passed
Push — 3.x ( 91f6f3...a4269d )
by Aleksei
03:22
created

Renderer   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 437
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 53
eloc 223
dl 0
loc 437
ccs 169
cts 169
cp 1
rs 6.96
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
B render() 0 40 10
A revertIndexes() 0 27 4
A revertForeignKeys() 0 28 4
A declareColumns() 0 25 4
A updateTable() 0 23 2
A indexOptions() 0 7 1
A createTable() 0 23 2
A revertTable() 0 16 1
A mountIndents() 0 9 2
A foreignKeyOptions() 0 10 1
A columnOptions() 0 26 6
A alterColumn() 0 25 3
A dropTable() 0 6 1
A declareIndexes() 0 27 4
A revertColumns() 0 25 4
A declareForeignKeys() 0 31 4

How to fix   Complexity   

Complex Class

Complex classes like Renderer 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.

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 Renderer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Migrations\Atomizer;
6
7
use Cycle\Database\Schema\AbstractColumn;
0 ignored issues
show
Bug introduced by
The type Cycle\Database\Schema\AbstractColumn was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Cycle\Database\Schema\AbstractForeignKey;
9
use Cycle\Database\Schema\AbstractIndex;
10
use Cycle\Database\Schema\AbstractTable;
11
use Cycle\Database\Schema\Comparator;
12
use Spiral\Reactor\Partial\Source;
13
use Spiral\Reactor\Serializer;
14
use Spiral\Reactor\Traits\SerializerTrait;
15
16
final class Renderer implements RendererInterface
17
{
18
    use SerializerTrait;
19
20
    /**
21
     * Comparator alteration states.
22
     */
23
    public const NEW_STATE = 0;
24
    public const ORIGINAL_STATE = 1;
25
26
    /**
27
     * {@inheritdoc}
28
     */
29 144
    public function createTable(Source $source, AbstractTable $table): void
30
    {
31 144
        $this->render(
32
            $source,
33
            '$this->table(%s)',
34
            $table
35
        );
36 144
        $comparator = $table->getComparator();
37
38 144
        $this->declareColumns($source, $comparator);
39 144
        $this->declareIndexes($source, $comparator);
40 144
        $this->declareForeignKeys($source, $comparator, $table->getPrefix());
41
42 144
        if (count($table->getPrimaryKeys())) {
43 136
            $this->render(
44
                $source,
45
                '    ->setPrimaryKeys(%s)',
46 136
                $table->getPrimaryKeys()
47
            );
48
        }
49
50
        //Finalization
51 144
        $source->addLine('    ->create();');
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 104
    public function updateTable(Source $source, AbstractTable $table): void
58
    {
59 104
        $this->render(
60
            $source,
61
            '$this->table(%s)',
62
            $table
63
        );
64 104
        $comparator = $table->getComparator();
65
66 104
        if ($comparator->isPrimaryChanged()) {
67 8
            $this->render(
68
                $source,
69
                '    ->setPrimaryKeys(%s)',
70 8
                $table->getPrimaryKeys()
71
            );
72
        }
73
74 104
        $this->declareColumns($source, $comparator);
75 104
        $this->declareIndexes($source, $comparator);
76 104
        $this->declareForeignKeys($source, $comparator, $table->getPrefix());
77
78
        //Finalization
79 104
        $source->addLine('    ->update();');
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 104
    public function revertTable(Source $source, AbstractTable $table): void
86
    {
87
        //Get table blueprint
88 104
        $this->render(
89
            $source,
90
            '$this->table(%s)',
91
            $table
92
        );
93 104
        $comparator = $table->getComparator();
94
95 104
        $this->revertForeignKeys($source, $comparator, $table->getPrefix());
96 104
        $this->revertIndexes($source, $comparator);
97 104
        $this->revertColumns($source, $comparator);
98
99
        //Finalization
100 104
        $source->addLine('    ->update();');
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 144
    public function dropTable(Source $source, AbstractTable $table): void
107
    {
108 144
        $this->render(
109
            $source,
110
            '$this->table(%s)->drop();',
111
            $table
112
        );
113
    }
114
115 144
    private function declareColumns(Source $source, Comparator $comparator): void
116
    {
117 144
        foreach ($comparator->addedColumns() as $column) {
118 144
            $this->render(
119
                $source,
120
                '    ->addColumn(%s, %s, %s)',
121 144
                $column->getName(),
122 144
                $column->getDeclaredType() ?? $column->getAbstractType(),
123
                $column
124
            );
125
        }
126
127 144
        foreach ($comparator->alteredColumns() as $pair) {
128 48
            $this->alterColumn(
129
                $source,
130 48
                $pair[self::NEW_STATE],
131 48
                $pair[self::ORIGINAL_STATE]
132
            );
133
        }
134
135 144
        foreach ($comparator->droppedColumns() as $column) {
136 16
            $this->render(
137
                $source,
138
                '    ->dropColumn(%s)',
139 16
                $column->getName()
140
            );
141
        }
142
    }
143
144 144
    private function declareIndexes(Source $source, Comparator $comparator): void
145
    {
146 144
        foreach ($comparator->addedIndexes() as $index) {
147 104
            $this->render(
148
                $source,
149
                '    ->addIndex(%s, %s)',
150 104
                $index->getColumns(),
151
                $index
152
            );
153
        }
154
155 144
        foreach ($comparator->alteredIndexes() as $pair) {
156
            /** @var AbstractIndex $index */
157 16
            $index = $pair[self::NEW_STATE];
158 16
            $this->render(
159
                $source,
160
                '    ->alterIndex(%s, %s)',
161 16
                $index->getColumns(),
162
                $index
163
            );
164
        }
165
166 144
        foreach ($comparator->droppedIndexes() as $index) {
167 8
            $this->render(
168
                $source,
169
                '    ->dropIndex(%s)',
170 8
                $index->getColumns()
171
            );
172
        }
173
    }
174
175
    /**
176
     * @param string $prefix Database isolation prefix
177
     */
178 144
    private function declareForeignKeys(Source $source, Comparator $comparator, string $prefix = ''): void
179
    {
180 144
        foreach ($comparator->addedForeignKeys() as $key) {
181 40
            $this->render(
182
                $source,
183
                '    ->addForeignKey(%s, %s, %s, %s)',
184 40
                $key->getColumns(),
185 40
                substr($key->getForeignTable(), strlen($prefix)),
186 40
                $key->getForeignKeys(),
187
                $key
188
            );
189
        }
190
191 144
        foreach ($comparator->alteredForeignKeys() as $pair) {
192
            /** @var AbstractForeignKey $key */
193 8
            $key = $pair[self::NEW_STATE];
194 8
            $this->render(
195
                $source,
196
                '    ->alterForeignKey(%s, %s, %s, %s)',
197 8
                $key->getColumns(),
198 8
                substr($key->getForeignTable(), strlen($prefix)),
199 8
                $key->getForeignKeys(),
200
                $key
201
            );
202
        }
203
204 144
        foreach ($comparator->droppedForeignKeys() as $key) {
205 8
            $this->render(
206
                $source,
207
                '    ->dropForeignKey(%s)',
208 8
                $key->getColumns()
209
            );
210
        }
211
    }
212
213 104
    private function revertColumns(Source $source, Comparator $comparator): void
214
    {
215 104
        foreach ($comparator->droppedColumns() as $column) {
216 16
            $this->render(
217
                $source,
218
                '    ->addColumn(%s, %s, %s)',
219 16
                $column->getName(),
220 16
                $column->getDeclaredType() ?? $column->getAbstractType(),
221
                $column
222
            );
223
        }
224
225 104
        foreach ($comparator->alteredColumns() as $pair) {
226 48
            $this->alterColumn(
227
                $source,
228 48
                $pair[self::ORIGINAL_STATE],
229 48
                $pair[self::NEW_STATE]
230
            );
231
        }
232
233 104
        foreach ($comparator->addedColumns() as $column) {
234 16
            $this->render(
235
                $source,
236
                '    ->dropColumn(%s)',
237 16
                $column->getName()
238
            );
239
        }
240
    }
241
242 104
    private function revertIndexes(Source $source, Comparator $comparator): void
243
    {
244 104
        foreach ($comparator->droppedIndexes() as $index) {
245 8
            $this->render(
246
                $source,
247
                '    ->addIndex(%s, %s)',
248 8
                $index->getColumns(),
249
                $index
250
            );
251
        }
252
253 104
        foreach ($comparator->alteredIndexes() as $pair) {
254
            /** @var AbstractIndex $index */
255 16
            $index = $pair[self::ORIGINAL_STATE];
256 16
            $this->render(
257
                $source,
258
                '    ->alterIndex(%s, %s)',
259 16
                $index->getColumns(),
260
                $index
261
            );
262
        }
263
264 104
        foreach ($comparator->addedIndexes() as $index) {
265 16
            $this->render(
266
                $source,
267
                '    ->dropIndex(%s)',
268 16
                $index->getColumns()
269
            );
270
        }
271
    }
272
273
    /**
274
     * @param string $prefix Database isolation prefix.
275
     */
276 104
    private function revertForeignKeys(Source $source, Comparator $comparator, string $prefix = ''): void
277
    {
278 104
        foreach ($comparator->droppedForeignKeys() as $key) {
279 8
            $this->render(
280
                $source,
281
                '    ->addForeignKey(%s, %s, %s, %s)',
282 8
                $key->getColumns(),
283 8
                substr($key->getForeignTable(), strlen($prefix)),
284 8
                $key->getForeignKeys(),
285
                $key
286
            );
287
        }
288
289 104
        foreach ($comparator->alteredForeignKeys() as $pair) {
290
            /** @var AbstractForeignKey $key */
291 8
            $key = $pair[self::ORIGINAL_STATE];
292 8
            $this->render(
293
                $source,
294
                '    ->alterForeignKey(%s, %s, %s, %s)',
295 8
                $key->getColumns(),
296 8
                substr($key->getForeignTable(), strlen($prefix)),
297 8
                $key->getForeignKeys(),
298
                $key
299
            );
300
        }
301
302 104
        foreach ($comparator->addedForeignKeys() as $key) {
303 8
            $this->render($source, '    ->dropForeignKey(%s)', $key->getColumns());
304
        }
305
    }
306
307 48
    protected function alterColumn(
308
        Source $source,
309
        AbstractColumn $column,
310
        AbstractColumn $original
311
    ): void {
312 48
        if ($column->getName() !== $original->getName()) {
313 16
            $name = $original->getName();
314
        } else {
315 32
            $name = $column->getName();
316
        }
317
318 48
        $this->render(
319
            $source,
320
            '    ->alterColumn(%s, %s, %s)',
321
            $name,
322 48
            $column->getDeclaredType() ?? $column->getAbstractType(),
323
            $column
324
        );
325
326 48
        if ($column->getName() !== $original->getName()) {
327 16
            $this->render(
328
                $source,
329
                '    ->renameColumn(%s, %s)',
330
                $name,
331 16
                $column->getName()
332
            );
333
        }
334
    }
335
336
    /**
337
     * Render values and options into source.
338
     *
339
     * @param array  ...$values
340
     */
341 144
    protected function render(Source $source, string $format, ...$values): void
342
    {
343 144
        $serializer = $this->getSerializer();
344
345 144
        $rendered = [];
346 144
        foreach ($values as $value) {
347 144
            if ($value instanceof AbstractTable) {
348 144
                $rendered[] = $serializer->serialize(
349 144
                    substr($value->getName(), strlen($value->getPrefix()))
350
                );
351 144
                continue;
352
            }
353
354 144
            if ($value instanceof AbstractColumn) {
355 144
                $rendered[] = $this->columnOptions($serializer, $value);
356 144
                continue;
357
            }
358
359 144
            if ($value instanceof AbstractIndex) {
360 104
                $rendered[] = $this->indexOptions($serializer, $value);
361 104
                continue;
362
            }
363
364 144
            if ($value instanceof AbstractForeignKey) {
365 40
                $rendered[] = $this->foreignKeyOptions($serializer, $value);
366 40
                continue;
367
            }
368
369
            // numeric array
370 144
            if (is_array($value) && count($value) > 0 && is_numeric(array_keys($value)[0])) {
371 136
                $rendered[] = '["' . implode('", "', $value) . '"]';
372 136
                continue;
373
            }
374
375 144
            $rendered[] = $serializer->serialize($value);
376
        }
377
378 144
        $lines = sprintf($format, ...$rendered);
379 144
        foreach (explode("\n", $lines) as $line) {
380 144
            $source->addLine($line);
381
        }
382
    }
383
384 144
    private function columnOptions(Serializer $serializer, AbstractColumn $column): string
385
    {
386 144
        $options = [
387 144
            'nullable' => $column->isNullable(),
388 144
            'default' => $column->getDefaultValue(),
389
        ];
390
391 144
        if ($column->getAbstractType() === 'enum') {
392 8
            $options['values'] = $column->getEnumValues();
393
        }
394
395 144
        if ($column->getAbstractType() === 'string') {
396 6
            $options['size'] = $column->getSize();
397
        }
398
399 144
        if ($column->getAbstractType() === 'decimal') {
400 8
            $options['scale'] = $column->getScale();
401 8
            $options['precision'] = $column->getPrecision();
402
        }
403
404 144
        $default = $options['default'];
405 144
        if ($column::DATETIME_NOW === ($default instanceof \Stringable ? (string)$default : $default)) {
406 8
            $options['default'] = AbstractColumn::DATETIME_NOW;
407
        }
408
409 144
        return $this->mountIndents($serializer->serialize($options));
410
    }
411
412 104
    private function indexOptions(Serializer $serializer, AbstractIndex $index): string
413
    {
414 104
        return $this->mountIndents(
415 104
            $serializer->serialize(
416
                [
417 104
                    'name' => $index->getName(),
418 104
                    'unique' => $index->isUnique(),
419
                ]
420
            )
421
        );
422
    }
423
424 40
    private function foreignKeyOptions(
425
        Serializer $serializer,
426
        AbstractForeignKey $reference
427
    ): string {
428 40
        return $this->mountIndents(
429 40
            $serializer->serialize(
430
                [
431 40
                    'name' => $reference->getName(),
432 40
                    'delete' => $reference->getDeleteRule(),
433 40
                    'update' => $reference->getUpdateRule(),
434
                ]
435
            )
436
        );
437
    }
438
439
    /**
440
     * Mount indents for column and index options.
441
     *
442
     * @param $serialized
443
     */
444 144
    private function mountIndents(string $serialized): string
445
    {
446 144
        $lines = explode("\n", $serialized);
447 144
        foreach ($lines as &$line) {
448 144
            $line = '    ' . $line;
449 144
            unset($line);
450
        }
451
452 144
        return ltrim(implode("\n", $lines));
453
    }
454
}
455