Passed
Pull Request — 3.x (#33)
by
unknown
11:41
created

Renderer::columnOptions()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7

Importance

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