1 | <?php |
||
2 | |||
3 | |||
4 | namespace carono\yii2migrate\traits; |
||
5 | |||
6 | use carono\yii2migrate\ForeignKeyColumn; |
||
7 | use carono\yii2migrate\helpers\SchemaHelper; |
||
8 | use carono\yii2migrate\IndexColumn; |
||
9 | use carono\yii2migrate\PivotColumn; |
||
10 | use yii\db\ColumnSchema; |
||
11 | use yii\db\ColumnSchemaBuilder; |
||
12 | use yii\db\Migration; |
||
13 | use yii\db\Schema; |
||
14 | use yii\helpers\ArrayHelper; |
||
15 | |||
16 | /** |
||
17 | * Trait MigrationTrait |
||
18 | * |
||
19 | * @package carono\yii2migrate\traits |
||
20 | * @mixin Migration |
||
21 | */ |
||
22 | trait MigrationTrait |
||
23 | { |
||
24 | private static $tableOptions = '@tableOptions'; |
||
25 | |||
26 | /** |
||
27 | * @param $refTable |
||
28 | * @param null $refColumn |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
29 | * |
||
30 | * @param string $type |
||
31 | * @param null $length |
||
0 ignored issues
–
show
|
|||
32 | * @return ForeignKeyColumn |
||
33 | */ |
||
34 | 44 | public function foreignKey($refTable = null, $refColumn = null, $type = Schema::TYPE_INTEGER, $length = null) |
|
35 | { |
||
36 | 44 | return (new ForeignKeyColumn($type, $length))->refTable($refTable)->refColumn($refColumn)->setMigrate($this); |
|
37 | } |
||
38 | |||
39 | /** |
||
40 | * @param array $columns |
||
41 | * @param bool $isUnique |
||
42 | * @return IndexColumn |
||
43 | */ |
||
44 | 7 | public function index($columns = [], $isUnique = false) |
|
45 | { |
||
46 | 7 | return (new IndexColumn())->setMigrate($this)->columns($columns)->unique($isUnique); |
|
47 | } |
||
48 | |||
49 | /** |
||
50 | * @param null $refTable |
||
0 ignored issues
–
show
|
|||
51 | * @param null $refColumn |
||
0 ignored issues
–
show
|
|||
52 | * |
||
53 | * @return PivotColumn |
||
54 | */ |
||
55 | 10 | public function pivot($refTable = null, $refColumn = null) |
|
56 | { |
||
57 | 10 | return (new PivotColumn())->refTable($refTable)->refColumn($refColumn)->setMigrate($this); |
|
58 | } |
||
59 | |||
60 | /** |
||
61 | * @return array |
||
62 | */ |
||
63 | 52 | public function tableOptions() |
|
64 | { |
||
65 | 52 | return []; |
|
66 | } |
||
67 | |||
68 | /** |
||
69 | * @param string $name |
||
70 | * @param string $table |
||
71 | * @param array|string $columns |
||
72 | * @param bool $unique |
||
73 | */ |
||
74 | 9 | public function createIndex($name, $table, $columns, $unique = false) |
|
75 | { |
||
76 | 9 | $suffix = $unique ? '_unq' : '_idx'; |
|
77 | 9 | if ($name === null) { |
|
78 | 2 | $name = self::formIndexName($table, $columns, $suffix, $this->db->tablePrefix); |
|
79 | 2 | $name = $this->expandTablePrefix($name); |
|
80 | } |
||
81 | 9 | $name = SchemaHelper::truncateIndexName($name, 64, $suffix); |
|
82 | 9 | parent::createIndex($name, $table, $columns, $unique); |
|
83 | 9 | } |
|
84 | |||
85 | /** |
||
86 | * @param ColumnSchema $column |
||
87 | * @return $this|ColumnSchemaBuilder |
||
88 | * @throws \Exception |
||
89 | */ |
||
90 | public function columnSchemaToBuilder(ColumnSchema $column) |
||
91 | { |
||
92 | $size = $column->size; |
||
93 | $precision = $column->precision; |
||
94 | $default = $column->defaultValue; |
||
95 | $scale = $column->scale; |
||
96 | if ($column->isPrimaryKey && $column->autoIncrement) { |
||
97 | return $this->primaryKey(); |
||
98 | } |
||
99 | switch ($column->type) { |
||
100 | case 'string': |
||
101 | $builder = $this->string($size); |
||
102 | break; |
||
103 | case 'integer': |
||
104 | $builder = $this->integer($size); |
||
105 | break; |
||
106 | case 'datetime': |
||
107 | $builder = $this->dateTime($precision); |
||
108 | break; |
||
109 | case 'text': |
||
110 | $builder = $this->text(); |
||
111 | break; |
||
112 | case 'smallint': |
||
113 | if ($size === 1) { |
||
114 | $default = (boolean)$default; |
||
115 | $builder = $this->boolean(); |
||
116 | } else { |
||
117 | $builder = $this->smallInteger($size); |
||
118 | } |
||
119 | break; |
||
120 | case 'binary': |
||
121 | $builder = $this->binary()->defaultValue($default); |
||
122 | break; |
||
123 | case 'decimal': |
||
124 | $builder = $this->decimal($precision, $scale); |
||
125 | break; |
||
126 | case 'double': |
||
127 | $builder = $this->double($precision)->defaultValue($default); |
||
128 | break; |
||
129 | default: |
||
130 | throw new \Exception("Column ($column->name) type '$column->type' not recognized"); |
||
131 | } |
||
132 | $builder->defaultValue($default); |
||
133 | if (!$column->allowNull) { |
||
134 | $builder->notNull(); |
||
135 | } |
||
136 | $builder->comment($column->comment); |
||
137 | return $builder; |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * @param $name |
||
142 | * @return mixed |
||
143 | */ |
||
144 | 52 | public function expandTablePrefix($name) |
|
145 | { |
||
146 | 52 | return SchemaHelper::expandTablePrefix($name, $this->db->tablePrefix); |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param string $table |
||
151 | * @param array $columns |
||
152 | * @param null $options |
||
0 ignored issues
–
show
|
|||
153 | * @throws \Exception |
||
154 | */ |
||
155 | 50 | public function createTable($table, $columns, $options = null) |
|
156 | { |
||
157 | /** |
||
158 | * @var PivotColumn[] $pvs |
||
159 | * @var ForeignKeyColumn[] $fks |
||
160 | */ |
||
161 | 50 | echo " > create table $table ..."; |
|
162 | 50 | $time = microtime(true); |
|
163 | 50 | $pvs = []; |
|
164 | 50 | $fks = []; |
|
165 | 50 | $pks = []; |
|
166 | |||
167 | 50 | $options = $this->getTableOptionsFromArray(ArrayHelper::remove($columns, self::$tableOptions, []), $options); |
|
168 | |||
169 | 50 | foreach ($columns as $column => &$type) { |
|
170 | |||
171 | 50 | if ($type instanceof ColumnSchema) { |
|
172 | $column = is_numeric($column) ? $type->name : $column; |
||
173 | $type = $this->columnSchemaToBuilder($type); |
||
174 | } |
||
175 | 50 | if ((string)$type === (string)$this->primaryKey()) { |
|
176 | 50 | $pks[] = $column; |
|
177 | } |
||
178 | 50 | if ($type instanceof ForeignKeyColumn) { |
|
179 | 27 | $type->sourceTable($table)->sourceColumn($column); |
|
180 | 27 | $fks[] = $type; |
|
181 | } |
||
182 | |||
183 | 50 | if ($type instanceof PivotColumn) { |
|
184 | 1 | $type->setSuffix($column)->sourceTable($table); |
|
185 | 1 | $pvs[] = $type; |
|
186 | 50 | unset($columns[$column]); |
|
187 | } |
||
188 | } |
||
189 | 50 | if (count($pks) > 1) { |
|
190 | 27 | foreach ($columns as $column => &$type) { |
|
191 | 27 | $type = $this->integer(); |
|
192 | } |
||
193 | } |
||
194 | 50 | $this->db->createCommand()->createTable($table, $columns, $options)->execute(); |
|
195 | 50 | foreach ($columns as $column => $type) { |
|
196 | 50 | if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) { |
|
197 | 50 | $this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute(); |
|
198 | } |
||
199 | } |
||
200 | 50 | if ($fks) { |
|
201 | 27 | echo "\n"; |
|
202 | } |
||
203 | 50 | foreach ($fks as $fk) { |
|
204 | 27 | echo ' '; |
|
205 | 27 | $fk->apply(); |
|
206 | } |
||
207 | 50 | if ($fks) { |
|
208 | 27 | echo "\n"; |
|
209 | } |
||
210 | 50 | if (count($pks) > 1) { |
|
211 | 27 | echo ' '; |
|
212 | 27 | $this->addPrimaryKey(null, $table, $pks); |
|
213 | } |
||
214 | 50 | if ($pvs) { |
|
215 | 1 | echo "\n"; |
|
216 | } |
||
217 | 50 | foreach ($pvs as $pv) { |
|
218 | 1 | echo ' '; |
|
219 | 1 | $pv->apply(); |
|
220 | } |
||
221 | 50 | echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n"; |
|
222 | 50 | } |
|
223 | |||
224 | /** |
||
225 | * @param string $name |
||
226 | * @param string $table |
||
227 | * @param array|string $columns |
||
228 | * @param string $refTable |
||
229 | * @param array|string $refColumns |
||
230 | * @param null $delete |
||
0 ignored issues
–
show
|
|||
231 | * @param null $update |
||
0 ignored issues
–
show
|
|||
232 | */ |
||
233 | 42 | public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) |
|
234 | { |
||
235 | 42 | if ($name === null) { |
|
236 | 42 | $name = $this->formFkName($table, $columns, $refTable, $refColumns); |
|
237 | 42 | $name = $this->expandTablePrefix($name); |
|
238 | } |
||
239 | 42 | $name = SchemaHelper::truncateIndexName($name, 64, '_fk'); |
|
240 | 42 | parent::addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update); |
|
241 | 42 | } |
|
242 | |||
243 | /** |
||
244 | * @inheritdoc |
||
245 | */ |
||
246 | 2 | public function alterColumn($table, $column, $type) |
|
247 | { |
||
248 | 2 | if ($type instanceof ForeignKeyColumn) { |
|
249 | 1 | $type->sourceTable($table); |
|
250 | 1 | $type->sourceColumn($column); |
|
251 | 1 | $type->apply(); |
|
252 | } else { |
||
253 | 1 | parent::alterColumn($table, $column, $type); |
|
254 | } |
||
255 | 2 | } |
|
256 | |||
257 | /** |
||
258 | * @inheritdoc |
||
259 | */ |
||
260 | 10 | public function addColumn($table, $column, $type) |
|
261 | { |
||
262 | 10 | if ($type instanceof ForeignKeyColumn) { |
|
263 | 2 | parent::addColumn($table, $column, $type); |
|
264 | 2 | $type->sourceTable($table); |
|
265 | 2 | $type->sourceColumn($column); |
|
266 | 2 | $type->apply(); |
|
267 | 8 | } elseif ($type instanceof PivotColumn) { |
|
268 | 1 | $type->sourceTable($table); |
|
269 | 1 | $type->setSuffix($column); |
|
270 | 1 | $type->apply(); |
|
271 | } else { |
||
272 | 7 | parent::addColumn($table, $column, $type); |
|
273 | } |
||
274 | 10 | } |
|
275 | |||
276 | /** |
||
277 | * @inheritdoc |
||
278 | */ |
||
279 | 32 | public function addPrimaryKey($name, $table, $columns) |
|
280 | { |
||
281 | 32 | if ($name === null) { |
|
282 | 32 | $name = self::formIndexName($table, $columns, '_pk', $this->db->tablePrefix); |
|
283 | 32 | $name = $this->expandTablePrefix($name); |
|
284 | } |
||
285 | 32 | $name = SchemaHelper::truncateIndexName($name, 64, '_pk'); |
|
286 | 32 | parent::addPrimaryKey($name, $table, $columns); |
|
287 | 32 | } |
|
288 | |||
289 | /** |
||
290 | * @return array |
||
291 | */ |
||
292 | 1 | public function newColumns() |
|
293 | { |
||
294 | 1 | return []; |
|
295 | } |
||
296 | |||
297 | /** |
||
298 | * @param array $array |
||
299 | */ |
||
300 | 9 | public function downNewColumns($array = []) |
|
301 | { |
||
302 | 9 | $this->_applyNewColumns($array ?: $this->newColumns(), true); |
|
303 | 9 | } |
|
304 | |||
305 | /** |
||
306 | * @param array $array |
||
307 | */ |
||
308 | 9 | public function upNewColumns($array = []) |
|
309 | { |
||
310 | 9 | $this->_applyNewColumns($array ?: $this->newColumns(), false); |
|
311 | 9 | } |
|
312 | |||
313 | /** |
||
314 | * @param array $columns |
||
315 | * @param bool $revert |
||
316 | */ |
||
317 | 9 | protected function _applyNewColumns($columns = [], $revert = false) |
|
318 | { |
||
319 | 9 | $columns = $revert ? array_reverse($columns) : $columns; |
|
320 | |||
321 | 9 | $result = []; |
|
322 | 9 | foreach ($columns as $key => $column) { |
|
323 | 9 | if (is_numeric($key)) { |
|
324 | $result[] = $column; |
||
325 | } else { |
||
326 | 9 | foreach ($column as $columnName => $value) { |
|
327 | 9 | $result[] = [$key, $columnName, $value]; |
|
328 | } |
||
329 | } |
||
330 | } |
||
331 | 9 | foreach ($result as $column) { |
|
332 | 9 | if ($column[2] instanceof PivotColumn) { |
|
333 | 2 | $column[2]->setSuffix($column[1])->sourceTable($column[0]); |
|
334 | } |
||
335 | 9 | if ($revert) { |
|
336 | 9 | if ($column[2] instanceof PivotColumn) { |
|
337 | 2 | $column[2]->remove(); |
|
338 | 2 | continue; |
|
339 | } |
||
340 | 7 | $this->dropColumn($column[0], $column[1]); |
|
341 | } else { |
||
342 | 9 | if ($column[2] instanceof PivotColumn) { |
|
343 | 2 | $column[2]->apply(); |
|
344 | 2 | continue; |
|
345 | } |
||
346 | 7 | $this->addColumn($column[0], $column[1], $column[2]); |
|
347 | } |
||
348 | } |
||
349 | 9 | } |
|
350 | |||
351 | /** |
||
352 | * @inheritdoc |
||
353 | */ |
||
354 | 9 | public function dropColumn($table, $column) |
|
355 | { |
||
356 | 9 | $foreignKeys = SchemaHelper::findTableForeignKeys($this->db, $table); |
|
357 | 9 | foreach ($foreignKeys as $key => $foreignKey) { |
|
358 | 8 | if ($foreignKey->columnNames === [$column]) { |
|
359 | 8 | $this->dropForeignKey($key, $table); |
|
360 | } |
||
361 | } |
||
362 | 9 | return parent::dropColumn($table, $column); |
|
363 | } |
||
364 | |||
365 | /** |
||
366 | * @return array |
||
367 | */ |
||
368 | 1 | public function newTables() |
|
369 | { |
||
370 | 1 | return []; |
|
371 | } |
||
372 | |||
373 | /** |
||
374 | * @param array $array |
||
375 | * @param null $tableOptions |
||
0 ignored issues
–
show
|
|||
376 | */ |
||
377 | 5 | public function upNewTables($array = [], $tableOptions = null) |
|
378 | { |
||
379 | 5 | $this->_applyNewTables($array ?: $this->newTables(), false, $tableOptions); |
|
380 | 5 | } |
|
381 | |||
382 | /** |
||
383 | * @param array $array |
||
384 | */ |
||
385 | 2 | public function upNewIndex($array = []) |
|
386 | { |
||
387 | 2 | $this->_applyNewIndex($array ?: $this->newIndex()); |
|
388 | 2 | } |
|
389 | |||
390 | /** |
||
391 | * @param array $array |
||
392 | */ |
||
393 | 2 | public function downNewIndex($array = []) |
|
394 | { |
||
395 | 2 | $this->_applyNewIndex($array ?: $this->newIndex(), true); |
|
396 | 2 | } |
|
397 | |||
398 | /** |
||
399 | * @return array |
||
400 | */ |
||
401 | 1 | public function newIndex() |
|
402 | { |
||
403 | 1 | return []; |
|
404 | } |
||
405 | |||
406 | /** |
||
407 | * @param array $array |
||
408 | */ |
||
409 | 5 | public function downNewTables($array = []) |
|
410 | { |
||
411 | 5 | $this->_applyNewTables($array ?: $this->newTables(), true); |
|
412 | 5 | } |
|
413 | |||
414 | /** |
||
415 | * @param $indexes |
||
416 | * @param bool $revert |
||
417 | */ |
||
418 | 2 | protected function _applyNewIndex($indexes, $revert = false) |
|
419 | { |
||
420 | /** |
||
421 | * @var IndexColumn $index |
||
422 | */ |
||
423 | 2 | $indexes = $revert ? array_reverse($indexes) : $indexes; |
|
424 | 2 | foreach ($indexes as $key => $data) { |
|
425 | 2 | if (!is_numeric($key)) { |
|
426 | 1 | foreach ($data as $index) { |
|
427 | 1 | if ($index instanceof IndexColumn) { |
|
428 | 1 | $index->table($key); |
|
429 | 1 | if ($revert) { |
|
430 | 1 | $index->remove(); |
|
431 | } else { |
||
432 | 1 | $index->apply(); |
|
433 | } |
||
434 | } |
||
435 | } |
||
436 | 1 | continue; |
|
437 | } |
||
438 | |||
439 | // Old style |
||
440 | 1 | $unq = isset($data[2]) && $data[2]; |
|
441 | 1 | $columns = is_array($data[1]) ? $data[1] : explode(',', $data[1]); |
|
442 | 1 | $index = $this->index($columns, $unq)->table($data[0]); |
|
443 | |||
444 | 1 | if ($revert) { |
|
445 | 1 | $index->remove(); |
|
446 | } else { |
||
447 | 1 | $index->apply(); |
|
448 | } |
||
449 | } |
||
450 | 2 | } |
|
451 | |||
452 | /** |
||
453 | * @param array|string $items |
||
454 | * @param string|array $default |
||
455 | * @return array|mixed|string |
||
456 | */ |
||
457 | 54 | private function getTableOptionsFromArray($items, $default = '') |
|
458 | { |
||
459 | 54 | if (is_array($default)) { |
|
460 | 2 | $default = ArrayHelper::getValue($default, $this->db->driverName, ''); |
|
461 | } |
||
462 | |||
463 | 54 | if (!$default) { |
|
464 | 51 | $default = ArrayHelper::getValue($this->tableOptions(), $this->db->driverName, ''); |
|
465 | } |
||
466 | |||
467 | 54 | if (is_array($items)) { |
|
468 | 52 | return ArrayHelper::getValue($items, $this->db->driverName, $default); |
|
469 | } |
||
470 | |||
471 | 4 | if ($items && is_string($items)) { |
|
472 | 3 | return $items; |
|
473 | } |
||
474 | |||
475 | 1 | return $default; |
|
476 | } |
||
477 | |||
478 | /** |
||
479 | * @param $tables |
||
480 | * @param bool $revert |
||
481 | * @param null $tableOptions |
||
0 ignored issues
–
show
|
|||
482 | */ |
||
483 | 5 | protected function _applyNewTables($tables, $revert = false, $tableOptions = null) |
|
484 | { |
||
485 | 5 | $tables = $revert ? array_reverse($tables) : $tables; |
|
486 | 5 | foreach ($tables as $table => $columns) { |
|
487 | 5 | if ($revert) { |
|
488 | 5 | foreach ($columns as $column => $type) { |
|
489 | 5 | if ($type instanceof PivotColumn) { |
|
490 | 1 | $type->setSuffix($column)->sourceTable($table); |
|
491 | 5 | $type->remove(); |
|
492 | } |
||
493 | } |
||
494 | 5 | $this->dropTable($table); |
|
495 | } else { |
||
496 | 5 | $this->createTable($table, $columns, $tableOptions); |
|
497 | } |
||
498 | } |
||
499 | 5 | } |
|
500 | |||
501 | /** |
||
502 | * @param $table |
||
503 | * @param $columns |
||
504 | * @param $refTable |
||
505 | * @param $refColumns |
||
506 | * @return string |
||
507 | */ |
||
508 | 43 | public function formFkName($table, $columns, $refTable, $refColumns) |
|
509 | { |
||
510 | 43 | return $this->foreignKey($refTable, $refColumns)->sourceTable($table)->sourceColumn($columns)->formIndexName(); |
|
511 | } |
||
512 | |||
513 | /** |
||
514 | * @param $table |
||
515 | * @param $columns |
||
516 | * @param string $suffix |
||
517 | * @param string $tablePrefix |
||
518 | * @return string |
||
519 | */ |
||
520 | 1 | public static function formPkIndexName($table, $columns, $suffix = '_pk', $tablePrefix = '') |
|
521 | { |
||
522 | 1 | return self::formIndexName($table, $columns, $suffix, $tablePrefix); |
|
523 | } |
||
524 | |||
525 | /** |
||
526 | * @param $table |
||
527 | * @param $columns |
||
528 | * @param string $suffix |
||
529 | * @param string $tablePrefix |
||
530 | * @return string |
||
531 | */ |
||
532 | 39 | public static function formIndexName($table, $columns, $suffix = '_idx', $tablePrefix = '') |
|
533 | { |
||
534 | 39 | $table = SchemaHelper::expandTablePrefix($table, $tablePrefix); |
|
535 | 39 | $table = SchemaHelper::removeSchema($table); |
|
536 | 39 | $column = implode(':', array_map('trim', (array)$columns)); |
|
537 | 39 | return "{$table}:{$column}$suffix"; |
|
538 | } |
||
539 | |||
540 | /** |
||
541 | * @param $table |
||
542 | * @param $column |
||
543 | */ |
||
544 | 1 | public function dropIndexByColumn($table, $column) |
|
545 | { |
||
546 | /** |
||
547 | * @var \yii\db\IndexConstraint $index |
||
548 | */ |
||
549 | 1 | foreach (SchemaHelper::findNonUniqueIndexes($this->db, $this->expandTablePrefix($table)) as $index) { |
|
550 | 1 | if ($index->columnNames === (array)$column) { |
|
551 | 1 | $this->dropIndex($index->name, $table); |
|
552 | } |
||
553 | } |
||
554 | 1 | } |
|
555 | |||
556 | /** |
||
557 | * @param $table |
||
558 | * @param $column |
||
559 | * @throws \yii\db\Exception |
||
560 | */ |
||
561 | 2 | public function dropForeignKeyByColumn($table, $column) |
|
562 | { |
||
563 | 2 | foreach (SchemaHelper::findTableForeignKeys($this->db, $table) as $key => $index) { |
|
564 | 2 | if ($index->columnNames === (array)$column) { |
|
565 | 2 | $this->dropForeignKey($key, $table); |
|
566 | } |
||
567 | } |
||
568 | } |
||
569 | } |