Completed
Branch feature/pre-split (67216b)
by Anton
03:22
created

MigrationRenderer::revertForeigns()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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