Completed
Branch feature/pre-split (c6befc)
by Anton
03:31
created

MigrationRenderer::revertColumns()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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