1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Spiral\Database\Schemas; |
10
|
|
|
|
11
|
|
|
use Psr\Log\LoggerAwareInterface; |
12
|
|
|
use Spiral\Database\Entities\Driver; |
13
|
|
|
use Spiral\Database\Schemas\ColumnInterface; |
14
|
|
|
use Spiral\Database\Schemas\TableInterface; |
15
|
|
|
use Spiral\Debug\Traits\LoggerTrait; |
16
|
|
|
use Spiral\ODM\Exceptions\SchemaException; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* AbstractTable class used to describe and manage state of specified table. It provides ability to |
20
|
|
|
* get table introspection, update table schema and automatically generate set of diff operations. |
21
|
|
|
* |
22
|
|
|
* Most of table operation like column, index or foreign key creation/altering will be applied when |
23
|
|
|
* save() method will be called. |
24
|
|
|
* |
25
|
|
|
* @todo Split operations and state representation. |
26
|
|
|
* |
27
|
|
|
* Column configuration shortcuts: |
28
|
|
|
* |
29
|
|
|
* @method AbstractColumn primary($column) |
30
|
|
|
* @method AbstractColumn bigPrimary($column) |
31
|
|
|
* @method AbstractColumn enum($column, array $values) |
32
|
|
|
* @method AbstractColumn string($column, $length = 255) |
33
|
|
|
* @method AbstractColumn decimal($column, $precision, $scale) |
34
|
|
|
* @method AbstractColumn boolean($column) |
35
|
|
|
* @method AbstractColumn integer($column) |
36
|
|
|
* @method AbstractColumn tinyInteger($column) |
37
|
|
|
* @method AbstractColumn bigInteger($column) |
38
|
|
|
* @method AbstractColumn text($column) |
39
|
|
|
* @method AbstractColumn tinyText($column) |
40
|
|
|
* @method AbstractColumn longText($column) |
41
|
|
|
* @method AbstractColumn double($column) |
42
|
|
|
* @method AbstractColumn float($column) |
43
|
|
|
* @method AbstractColumn datetime($column) |
44
|
|
|
* @method AbstractColumn date($column) |
45
|
|
|
* @method AbstractColumn time($column) |
46
|
|
|
* @method AbstractColumn timestamp($column) |
47
|
|
|
* @method AbstractColumn binary($column) |
48
|
|
|
* @method AbstractColumn tinyBinary($column) |
49
|
|
|
* @method AbstractColumn longBinary($column) |
50
|
|
|
*/ |
51
|
|
|
abstract class AbstractTable extends TableState implements TableInterface, LoggerAwareInterface |
52
|
|
|
{ |
53
|
|
|
use LoggerTrait; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Indication that table is exists and current schema is fetched from database. |
57
|
|
|
* |
58
|
|
|
* @var bool |
59
|
|
|
*/ |
60
|
|
|
private $exists = false; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Database specific tablePrefix. Required for table renames. |
64
|
|
|
* |
65
|
|
|
* @var string |
66
|
|
|
*/ |
67
|
|
|
private $prefix = ''; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* We have to remember original schema state to create set of diff based commands. |
71
|
|
|
* |
72
|
|
|
* @invisible |
73
|
|
|
* |
74
|
|
|
* @var TableState |
75
|
|
|
*/ |
76
|
|
|
protected $initial = null; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Compares current and original states. |
80
|
|
|
* |
81
|
|
|
* @invisible |
82
|
|
|
* |
83
|
|
|
* @var Comparator |
84
|
|
|
*/ |
85
|
|
|
protected $comparator = null; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @invisible |
89
|
|
|
* |
90
|
|
|
* @var Driver |
91
|
|
|
*/ |
92
|
|
|
protected $driver = null; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Executes table operations. |
96
|
|
|
* |
97
|
|
|
* @var AbstractCommander |
98
|
|
|
*/ |
99
|
|
|
protected $commander = null; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @param Driver $driver Parent driver. |
103
|
|
|
* @param AbstractCommander $commander |
104
|
|
|
* @param string $name Table name, must include table prefix. |
105
|
|
|
* @param string $prefix Database specific table prefix. |
106
|
|
|
*/ |
107
|
|
|
public function __construct(Driver $driver, AbstractCommander $commander, $name, $prefix) |
108
|
|
|
{ |
109
|
|
|
parent::__construct($name); |
110
|
|
|
|
111
|
|
|
$this->driver = $driver; |
112
|
|
|
$this->commander = $commander; |
113
|
|
|
|
114
|
|
|
$this->prefix = $prefix; |
115
|
|
|
|
116
|
|
|
//Locking down initial table state |
117
|
|
|
$this->initial = new TableState($name); |
118
|
|
|
|
119
|
|
|
//Needed to compare schemas |
120
|
|
|
$this->comparator = new Comparator($this->initial, $this); |
121
|
|
|
|
122
|
|
|
if (!$this->driver->hasTable($this->getName())) { |
123
|
|
|
//There is no need to load table schema when table does not exist |
124
|
|
|
return; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
//Loading table information |
128
|
|
|
$this->loadColumns()->loadIndexes()->loadReferences(); |
129
|
|
|
|
130
|
|
|
//Syncing schemas |
131
|
|
|
$this->initial->syncSchema($this); |
132
|
|
|
|
133
|
|
|
$this->exists = true; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Get associated table driver. |
138
|
|
|
* |
139
|
|
|
* @return Driver |
140
|
|
|
*/ |
141
|
|
|
public function driver() |
142
|
|
|
{ |
143
|
|
|
return $this->driver; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Get table comparator. |
148
|
|
|
* |
149
|
|
|
* @return Comparator |
150
|
|
|
*/ |
151
|
|
|
public function comparator() |
152
|
|
|
{ |
153
|
|
|
return $this->comparator; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* {@inheritdoc} |
158
|
|
|
*/ |
159
|
|
|
public function exists() |
160
|
|
|
{ |
161
|
|
|
return $this->exists; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* {@inheritdoc} |
166
|
|
|
* |
167
|
|
|
* Automatically forces prefix value. |
168
|
|
|
*/ |
169
|
|
|
public function setName($name) |
170
|
|
|
{ |
171
|
|
|
parent::setName($this->prefix . $name); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* {@inheritdoc} |
176
|
|
|
* |
177
|
|
|
* @param bool $quoted Quote name. |
178
|
|
|
*/ |
179
|
|
|
public function getName($quoted = false) |
180
|
|
|
{ |
181
|
|
|
if (!$quoted) { |
182
|
|
|
return parent::getName(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
return $this->driver->identifier(parent::getName()); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Return database specific table prefix. |
190
|
|
|
* |
191
|
|
|
* @return string |
192
|
|
|
*/ |
193
|
|
|
public function getPrefix() |
194
|
|
|
{ |
195
|
|
|
return $this->prefix; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* {@inheritdoc} |
200
|
|
|
*/ |
201
|
|
|
public function getDependencies() |
202
|
|
|
{ |
203
|
|
|
$tables = []; |
204
|
|
|
foreach ($this->getForeigns() as $foreign) { |
205
|
|
|
$tables[] = $foreign->getForeignTable(); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
return $tables; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Set table primary keys. Operation can only be applied for newly created tables. Now every |
213
|
|
|
* database might support compound indexes. |
214
|
|
|
* |
215
|
|
|
* @param array $columns |
216
|
|
|
* |
217
|
|
|
* @return $this |
218
|
|
|
* |
219
|
|
|
* @throws SchemaException |
220
|
|
|
*/ |
221
|
|
|
public function setPrimaryKeys(array $columns) |
222
|
|
|
{ |
223
|
|
|
if ($this->exists() && $this->getPrimaryKeys() != $columns) { |
224
|
|
|
throw new SchemaException('Unable to change primary keys for already exists table'); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
parent::setPrimaryKeys($columns); |
228
|
|
|
|
229
|
|
|
return $this; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Get/create instance of AbstractColumn associated with current table. |
234
|
|
|
* |
235
|
|
|
* Examples: |
236
|
|
|
* $table->column('name')->string(); |
237
|
|
|
* |
238
|
|
|
* @param string $name |
239
|
|
|
* |
240
|
|
|
* @return AbstractColumn |
241
|
|
|
*/ |
242
|
|
|
public function column($name) |
243
|
|
|
{ |
244
|
|
|
if (!empty($column = $this->findColumn($name))) { |
245
|
|
|
return $column->declared(true); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
$column = $this->columnSchema($name)->declared(true); |
249
|
|
|
|
250
|
|
|
//Registering (without adding to initial schema) |
251
|
|
|
return $this->registerColumn($column); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Get/create instance of AbstractIndex associated with current table based on list of forming |
256
|
|
|
* column names. |
257
|
|
|
* |
258
|
|
|
* Example: |
259
|
|
|
* $table->index('key'); |
260
|
|
|
* $table->index('key', 'key2'); |
261
|
|
|
* $table->index(['key', 'key2']); |
262
|
|
|
* |
263
|
|
|
* @param mixed $columns Column name, or array of columns. |
264
|
|
|
* |
265
|
|
|
* @return AbstractIndex |
266
|
|
|
*/ |
267
|
|
|
public function index($columns) |
268
|
|
|
{ |
269
|
|
|
$columns = is_array($columns) ? $columns : func_get_args(); |
270
|
|
|
if (!empty($index = $this->findIndex($columns))) { |
271
|
|
|
return $index->declared(true); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
$index = $this->indexSchema(null)->declared(true); |
275
|
|
|
$index->columns($columns)->unique(false); |
276
|
|
|
|
277
|
|
|
return $this->registerIndex($index); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Get/create instance of AbstractIndex associated with current table based on list of forming |
282
|
|
|
* column names. Index type must be forced as UNIQUE. |
283
|
|
|
* |
284
|
|
|
* Example: |
285
|
|
|
* $table->unique('key'); |
286
|
|
|
* $table->unique('key', 'key2'); |
287
|
|
|
* $table->unique(['key', 'key2']); |
288
|
|
|
* |
289
|
|
|
* @param mixed $columns Column name, or array of columns. |
290
|
|
|
* |
291
|
|
|
* @return AbstractColumn|null |
292
|
|
|
*/ |
293
|
|
|
public function unique($columns) |
294
|
|
|
{ |
295
|
|
|
$columns = is_array($columns) ? $columns : func_get_args(); |
296
|
|
|
|
297
|
|
|
return $this->index($columns)->unique(true); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Get/create instance of AbstractReference associated with current table based on local column |
302
|
|
|
* name. |
303
|
|
|
* |
304
|
|
|
* @param string $column Column name. |
305
|
|
|
* |
306
|
|
|
* @return AbstractReference|null |
307
|
|
|
*/ |
308
|
|
|
public function foreign($column) |
309
|
|
|
{ |
310
|
|
|
if (!empty($foreign = $this->findForeign($column))) { |
311
|
|
|
return $foreign->declared(true); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
$foreign = $this->referenceSchema(null)->declared(true); |
315
|
|
|
$foreign->column($column); |
316
|
|
|
|
317
|
|
|
return $this->registerReference($foreign); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Rename column (only if column exists). |
322
|
|
|
* |
323
|
|
|
* @param string $column |
324
|
|
|
* @param string $name New column name. |
325
|
|
|
* |
326
|
|
|
* @return $this |
327
|
|
|
*/ |
328
|
|
|
public function renameColumn($column, $name) |
329
|
|
|
{ |
330
|
|
|
if (empty($column = $this->findColumn($column))) { |
331
|
|
|
return $this; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
//Renaming automatically declares column |
335
|
|
|
$column->declared(true)->setName($name); |
336
|
|
|
|
337
|
|
|
return $this; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Rename index (only if index exists). |
342
|
|
|
* |
343
|
|
|
* @param array $columns Index forming columns. |
344
|
|
|
* @param string $name New index name. |
345
|
|
|
* |
346
|
|
|
* @return $this |
347
|
|
|
*/ |
348
|
|
|
public function renameIndex(array $columns, $name) |
349
|
|
|
{ |
350
|
|
|
if (empty($index = $this->findIndex($columns))) { |
351
|
|
|
return $this; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
//Renaming automatically declares index |
355
|
|
|
$index->declared(true)->setName($name); |
356
|
|
|
|
357
|
|
|
return $this; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Drop column by it's name. |
362
|
|
|
* |
363
|
|
|
* @param string $column |
364
|
|
|
* |
365
|
|
|
* @return $this |
366
|
|
|
*/ |
367
|
|
|
public function dropColumn($column) |
368
|
|
|
{ |
369
|
|
|
if (!empty($column = $this->findColumn($column))) { |
370
|
|
|
$this->forgetColumn($column); |
371
|
|
|
$this->removeDependent($column); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
return $this; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Drop index by it's forming columns. |
379
|
|
|
* |
380
|
|
|
* @param array $columns |
381
|
|
|
* |
382
|
|
|
* @return $this |
383
|
|
|
*/ |
384
|
|
|
public function dropIndex(array $columns) |
385
|
|
|
{ |
386
|
|
|
if (!empty($index = $this->findIndex($columns))) { |
387
|
|
|
$this->forgetIndex($index); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
return $this; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Drop foreign key by it's name. |
395
|
|
|
* |
396
|
|
|
* @param string $column |
397
|
|
|
* |
398
|
|
|
* @return $this |
399
|
|
|
*/ |
400
|
|
|
public function dropForeign($column) |
401
|
|
|
{ |
402
|
|
|
if (!empty($foreign = $this->findForeign($column))) { |
403
|
|
|
$this->forgetForeign($foreign); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
return $this; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Shortcut for column() method. |
411
|
|
|
* |
412
|
|
|
* @param string $column |
413
|
|
|
* |
414
|
|
|
* @return AbstractColumn |
415
|
|
|
*/ |
416
|
|
|
public function __get($column) |
417
|
|
|
{ |
418
|
|
|
return $this->column($column); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Column creation/altering shortcut, call chain is identical to: |
423
|
|
|
* AbstractTable->column($name)->$type($arguments). |
424
|
|
|
* |
425
|
|
|
* Example: |
426
|
|
|
* $table->string("name"); |
427
|
|
|
* $table->text("some_column"); |
428
|
|
|
* |
429
|
|
|
* @param string $type |
430
|
|
|
* @param array $arguments Type specific parameters. |
431
|
|
|
* |
432
|
|
|
* @return AbstractColumn |
433
|
|
|
*/ |
434
|
|
|
public function __call($type, array $arguments) |
435
|
|
|
{ |
436
|
|
|
return call_user_func_array( |
437
|
|
|
[$this->column($arguments[0]), $type], |
438
|
|
|
array_slice($arguments, 1) |
439
|
|
|
); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* Declare every existed element. Method has to be called if table modification applied to |
444
|
|
|
* existed table to prevent dropping of existed elements. |
445
|
|
|
* |
446
|
|
|
* @return $this |
447
|
|
|
*/ |
448
|
|
|
public function declareExisted() |
449
|
|
|
{ |
450
|
|
|
foreach ($this->getColumns() as $column) { |
451
|
|
|
$column->declared(true); |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
foreach ($this->getIndexes() as $index) { |
455
|
|
|
$index->declared(true); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
foreach ($this->getForeigns() as $foreign) { |
459
|
|
|
$foreign->declared(true); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
return $this; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Calculate difference (removed columns, indexes and foreign keys). |
467
|
|
|
* |
468
|
|
|
* @param bool $forgetColumns |
469
|
|
|
* @param bool $forgetIndexes |
470
|
|
|
* @param bool $forgetForeigns |
471
|
|
|
*/ |
472
|
|
|
public function forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns) |
473
|
|
|
{ |
474
|
|
|
//We don't need to worry about changed or created columns, indexes and foreign keys here |
475
|
|
|
//as it already handled, we only have to drop columns which were not listed in schema |
476
|
|
|
|
477
|
|
|
foreach ($this->getColumns() as $column) { |
478
|
|
|
if ($forgetColumns && !$column->isDeclared()) { |
479
|
|
|
$this->forgetColumn($column); |
480
|
|
|
$this->removeDependent($column); |
481
|
|
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
foreach ($this->getIndexes() as $index) { |
485
|
|
|
if ($forgetIndexes && !$index->isDeclared()) { |
486
|
|
|
$this->forgetIndex($index); |
487
|
|
|
} |
488
|
|
|
} |
489
|
|
|
|
490
|
|
|
foreach ($this->getForeigns() as $foreign) { |
491
|
|
|
if ($forgetForeigns && !$foreign->isDeclared()) { |
492
|
|
|
$this->forgetForeign($foreign); |
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
/** |
498
|
|
|
* Save table schema including every column, index, foreign key creation/altering. If table does |
499
|
|
|
* not exist it must be created. |
500
|
|
|
* |
501
|
|
|
* @param bool $forgetColumns Drop all non declared columns. |
502
|
|
|
* @param bool $forgetIndexes Drop all non declared indexes. |
503
|
|
|
* @param bool $forgetForeigns Drop all non declared foreign keys. |
504
|
|
|
*/ |
505
|
|
|
public function save($forgetColumns = true, $forgetIndexes = true, $forgetForeigns = true) |
506
|
|
|
{ |
507
|
|
|
if (!$this->exists()) { |
508
|
|
|
$this->createSchema(); |
509
|
|
|
} else { |
510
|
|
|
//Let's remove from schema elements which wasn't declared |
511
|
|
|
$this->forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns); |
512
|
|
|
|
513
|
|
|
if ($this->hasChanges()) { |
514
|
|
|
$this->synchroniseSchema(); |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
//Syncing internal states |
519
|
|
|
$this->initial->syncSchema($this); |
520
|
|
|
$this->exists = true; |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
/** |
524
|
|
|
* Drop table schema in database. This operation must be applied immediately. |
525
|
|
|
*/ |
526
|
|
|
public function drop() |
527
|
|
|
{ |
528
|
|
|
$this->forgetElements(); |
529
|
|
|
|
530
|
|
|
//Re-syncing initial state |
531
|
|
|
$this->initial->syncSchema($this->forgetElements()); |
532
|
|
|
|
533
|
|
|
if ($this->exists()) { |
534
|
|
|
$this->commander->dropTable($this->getName()); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
$this->exists = false; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* @return AbstractColumn|string |
542
|
|
|
*/ |
543
|
|
|
public function __toString() |
544
|
|
|
{ |
545
|
|
|
return $this->getName(); |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* @return array |
550
|
|
|
*/ |
551
|
|
|
public function __debugInfo() |
552
|
|
|
{ |
553
|
|
|
return [ |
554
|
|
|
'name' => $this->getName(), |
555
|
|
|
'primaryKeys' => $this->getPrimaryKeys(), |
556
|
|
|
'columns' => array_values($this->getColumns()), |
557
|
|
|
'indexes' => array_values($this->getIndexes()), |
558
|
|
|
'references' => array_values($this->getForeigns()), |
559
|
|
|
]; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Create table. |
564
|
|
|
*/ |
565
|
|
|
protected function createSchema() |
566
|
|
|
{ |
567
|
|
|
$this->logger()->debug('Creating new table {table}.', ['table' => $this->getName(true)]); |
568
|
|
|
|
569
|
|
|
$this->commander->createTable($this); |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Execute schema update. |
574
|
|
|
*/ |
575
|
|
|
protected function synchroniseSchema() |
576
|
|
|
{ |
577
|
|
|
if ($this->getName() != $this->initial->getName()) { |
578
|
|
|
//Executing renaming |
579
|
|
|
$this->commander->renameTable($this->initial->getName(), $this->getName()); |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
//Some data has to be dropped before column updates |
583
|
|
|
$this->dropForeigns()->dropIndexes(); |
584
|
|
|
|
585
|
|
|
//Generate update flow |
586
|
|
|
$this->synchroniseColumns()->synchroniseIndexes()->synchroniseForeigns(); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Synchronise columns. |
591
|
|
|
* |
592
|
|
|
* @todo Split or isolate. |
593
|
|
|
* @return $this |
594
|
|
|
*/ |
595
|
|
|
protected function synchroniseColumns() |
596
|
|
|
{ |
597
|
|
|
foreach ($this->comparator->droppedColumns() as $column) { |
598
|
|
|
$this->logger()->debug('Dropping column [{statement}] from table {table}.', [ |
599
|
|
|
'statement' => $column->sqlStatement(), |
600
|
|
|
'table' => $this->getName(true), |
601
|
|
|
]); |
602
|
|
|
|
603
|
|
|
$this->commander->dropColumn($this, $column); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
foreach ($this->comparator->addedColumns() as $column) { |
607
|
|
|
$this->logger()->debug('Adding column [{statement}] into table {table}.', [ |
608
|
|
|
'statement' => $column->sqlStatement(), |
609
|
|
|
'table' => $this->getName(true), |
610
|
|
|
]); |
611
|
|
|
|
612
|
|
|
$this->commander->addColumn($this, $column); |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
foreach ($this->comparator->alteredColumns() as $pair) { |
616
|
|
|
/** |
617
|
|
|
* @var AbstractColumn $initial |
618
|
|
|
* @var AbstractColumn $current |
619
|
|
|
*/ |
620
|
|
|
list($current, $initial) = $pair; |
621
|
|
|
|
622
|
|
|
$this->logger()->debug('Altering column [{statement}] to [{new}] in table {table}.', [ |
623
|
|
|
'statement' => $initial->sqlStatement(), |
624
|
|
|
'new' => $current->sqlStatement(), |
625
|
|
|
'table' => $this->getName(true), |
626
|
|
|
]); |
627
|
|
|
|
628
|
|
|
$this->commander->alterColumn($this, $initial, $current); |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
return $this; |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* Drop needed indexes. |
636
|
|
|
* |
637
|
|
|
* @return $this |
638
|
|
|
*/ |
639
|
|
View Code Duplication |
protected function dropIndexes() |
|
|
|
|
640
|
|
|
{ |
641
|
|
|
foreach ($this->comparator->droppedIndexes() as $index) { |
642
|
|
|
$this->logger()->debug('Dropping index [{statement}] from table {table}.', [ |
643
|
|
|
'statement' => $index->sqlStatement(), |
644
|
|
|
'table' => $this->getName(true), |
645
|
|
|
]); |
646
|
|
|
|
647
|
|
|
$this->commander->dropIndex($this, $index); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
return $this; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* Synchronise indexes. |
655
|
|
|
* |
656
|
|
|
* @return $this |
657
|
|
|
*/ |
658
|
|
View Code Duplication |
protected function synchroniseIndexes() |
|
|
|
|
659
|
|
|
{ |
660
|
|
|
foreach ($this->comparator->addedIndexes() as $index) { |
661
|
|
|
$this->logger()->debug('Adding index [{statement}] into table {table}.', [ |
662
|
|
|
'statement' => $index->sqlStatement(), |
663
|
|
|
'table' => $this->getName(true), |
664
|
|
|
]); |
665
|
|
|
|
666
|
|
|
$this->commander->addIndex($this, $index); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
foreach ($this->comparator->alteredIndexes() as $pair) { |
670
|
|
|
/** |
671
|
|
|
* @var AbstractIndex $initial |
672
|
|
|
* @var AbstractIndex $current |
673
|
|
|
*/ |
674
|
|
|
list($current, $initial) = $pair; |
675
|
|
|
|
676
|
|
|
$this->logger()->debug('Altering index [{statement}] to [{new}] in table {table}.', [ |
677
|
|
|
'statement' => $initial->sqlStatement(), |
678
|
|
|
'new' => $current->sqlStatement(), |
679
|
|
|
'table' => $this->getName(true), |
680
|
|
|
]); |
681
|
|
|
|
682
|
|
|
$this->commander->alterIndex($this, $initial, $current); |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
return $this; |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
/** |
689
|
|
|
* Drop needed foreign keys. |
690
|
|
|
* |
691
|
|
|
* @return $this |
692
|
|
|
*/ |
693
|
|
View Code Duplication |
protected function dropForeigns() |
|
|
|
|
694
|
|
|
{ |
695
|
|
|
foreach ($this->comparator->droppedForeigns() as $foreign) { |
696
|
|
|
$this->logger()->debug('Dropping foreign key [{statement}] from table {table}.', [ |
697
|
|
|
'statement' => $foreign->sqlStatement(), |
698
|
|
|
'table' => $this->getName(true), |
699
|
|
|
]); |
700
|
|
|
|
701
|
|
|
$this->commander->dropForeign($this, $foreign); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
return $this; |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
/** |
708
|
|
|
* Synchronise foreign keys. |
709
|
|
|
* |
710
|
|
|
* @return $this |
711
|
|
|
*/ |
712
|
|
View Code Duplication |
protected function synchroniseForeigns() |
|
|
|
|
713
|
|
|
{ |
714
|
|
|
foreach ($this->comparator->addedForeigns() as $foreign) { |
715
|
|
|
$this->logger()->debug('Adding foreign key [{statement}] into table {table}.', [ |
716
|
|
|
'statement' => $foreign->sqlStatement(), |
717
|
|
|
'table' => $this->getName(true), |
718
|
|
|
]); |
719
|
|
|
|
720
|
|
|
$this->commander->addForeign($this, $foreign); |
721
|
|
|
} |
722
|
|
|
|
723
|
|
|
foreach ($this->comparator->alteredForeigns() as $pair) { |
724
|
|
|
/** |
725
|
|
|
* @var AbstractReference $initial |
726
|
|
|
* @var AbstractReference $current |
727
|
|
|
*/ |
728
|
|
|
list($current, $initial) = $pair; |
729
|
|
|
|
730
|
|
|
$this->logger()->debug('Altering foreign key [{statement}] to [{new}] in {table}.', [ |
731
|
|
|
'statement' => $initial->sqlStatement(), |
732
|
|
|
'table' => $this->getName(true), |
733
|
|
|
]); |
734
|
|
|
|
735
|
|
|
$this->commander->alterForeign($this, $initial, $current); |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
return $this; |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
/** |
742
|
|
|
* Driver specific column schema. |
743
|
|
|
* |
744
|
|
|
* @param string $name |
745
|
|
|
* @param mixed $schema |
746
|
|
|
* |
747
|
|
|
* @return AbstractColumn |
748
|
|
|
*/ |
749
|
|
|
abstract protected function columnSchema($name, $schema = null); |
750
|
|
|
|
751
|
|
|
/** |
752
|
|
|
* Driver specific index schema. |
753
|
|
|
* |
754
|
|
|
* @param string $name |
755
|
|
|
* @param mixed $schema |
756
|
|
|
* |
757
|
|
|
* @return AbstractIndex |
758
|
|
|
*/ |
759
|
|
|
abstract protected function indexSchema($name, $schema = null); |
760
|
|
|
|
761
|
|
|
/** |
762
|
|
|
* Driver specific reference schema. |
763
|
|
|
* |
764
|
|
|
* @param string $name |
765
|
|
|
* @param mixed $schema |
766
|
|
|
* |
767
|
|
|
* @return AbstractReference |
768
|
|
|
*/ |
769
|
|
|
abstract protected function referenceSchema($name, $schema = null); |
770
|
|
|
|
771
|
|
|
/** |
772
|
|
|
* Must load table columns. |
773
|
|
|
* |
774
|
|
|
* @see registerColumn() |
775
|
|
|
* |
776
|
|
|
* @return self |
777
|
|
|
*/ |
778
|
|
|
abstract protected function loadColumns(); |
779
|
|
|
|
780
|
|
|
/** |
781
|
|
|
* Must load table indexes. |
782
|
|
|
* |
783
|
|
|
* @see registerIndex() |
784
|
|
|
* |
785
|
|
|
* @return self |
786
|
|
|
*/ |
787
|
|
|
abstract protected function loadIndexes(); |
788
|
|
|
|
789
|
|
|
/** |
790
|
|
|
* Must load table references. |
791
|
|
|
* |
792
|
|
|
* @see registerReference() |
793
|
|
|
* |
794
|
|
|
* @return self |
795
|
|
|
*/ |
796
|
|
|
abstract protected function loadReferences(); |
797
|
|
|
|
798
|
|
|
/** |
799
|
|
|
* Check if table schema has been modified. Attention, you have to execute dropUndeclared first |
800
|
|
|
* to get valid results. |
801
|
|
|
* |
802
|
|
|
* @return bool |
803
|
|
|
*/ |
804
|
|
|
protected function hasChanges() |
805
|
|
|
{ |
806
|
|
|
return $this->comparator->hasChanges(); |
807
|
|
|
} |
808
|
|
|
|
809
|
|
|
/** |
810
|
|
|
* Remove dependent indexes and foreign keys. |
811
|
|
|
* |
812
|
|
|
* @param ColumnInterface $column |
813
|
|
|
*/ |
814
|
|
|
private function removeDependent(ColumnInterface $column) |
815
|
|
|
{ |
816
|
|
|
if ($this->hasForeign($column->getName())) { |
817
|
|
|
$this->forgetForeign($this->foreign($column->getName())); |
|
|
|
|
818
|
|
|
} |
819
|
|
|
|
820
|
|
|
foreach ($this->getIndexes() as $index) { |
821
|
|
|
if (in_array($column->getName(), $index->getColumns())) { |
822
|
|
|
//Dropping related indexes |
823
|
|
|
$this->forgetIndex($index); |
824
|
|
|
} |
825
|
|
|
} |
826
|
|
|
} |
827
|
|
|
|
828
|
|
|
/** |
829
|
|
|
* Forget all elements. |
830
|
|
|
* |
831
|
|
|
* @return $this |
832
|
|
|
*/ |
833
|
|
|
private function forgetElements() |
834
|
|
|
{ |
835
|
|
|
foreach ($this->getColumns() as $column) { |
836
|
|
|
$this->forgetColumn($column); |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
foreach ($this->getIndexes() as $index) { |
840
|
|
|
$this->forgetIndex($index); |
841
|
|
|
} |
842
|
|
|
|
843
|
|
|
foreach ($this->getForeigns() as $foreign) { |
844
|
|
|
$this->forgetForeign($foreign); |
845
|
|
|
} |
846
|
|
|
|
847
|
|
|
return $this; |
848
|
|
|
} |
849
|
|
|
} |
850
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.