Completed
Branch develop (c2aa4c)
by Anton
05:17
created

AbstractTable::forgetUndeclared()   C

Complexity

Conditions 10
Paths 27

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 5.2165
cc 10
eloc 11
nc 27
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Database\Entities\Schemas;
9
10
use Psr\Log\LoggerAwareInterface;
11
use Spiral\Database\Entities\Driver;
12
use Spiral\Database\Schemas\ColumnInterface;
13
use Spiral\Database\Schemas\TableInterface;
14
use Spiral\Debug\Traits\LoggerTrait;
15
use Spiral\ODM\Exceptions\SchemaException;
16
17
/**
18
 * AbstractTable class used to describe and manage state of specified table. It provides ability to
19
 * get table introspection, update table schema and automatically generate set of diff operations.
20
 *
21
 * Most of table operation like column, index or foreign key creation/altering will be applied when
22
 * save() method will be called.
23
 *
24
 * Column configuration shortcuts:
25
 * @method AbstractColumn primary($column)
26
 * @method AbstractColumn bigPrimary($column)
27
 * @method AbstractColumn enum($column, array $values)
28
 * @method AbstractColumn string($column, $length = 255)
29
 * @method AbstractColumn decimal($column, $precision, $scale)
30
 * @method AbstractColumn boolean($column)
31
 * @method AbstractColumn integer($column)
32
 * @method AbstractColumn tinyInteger($column)
33
 * @method AbstractColumn bigInteger($column)
34
 * @method AbstractColumn text($column)
35
 * @method AbstractColumn tinyText($column)
36
 * @method AbstractColumn longText($column)
37
 * @method AbstractColumn double($column)
38
 * @method AbstractColumn float($column)
39
 * @method AbstractColumn datetime($column)
40
 * @method AbstractColumn date($column)
41
 * @method AbstractColumn time($column)
42
 * @method AbstractColumn timestamp($column)
43
 * @method AbstractColumn binary($column)
44
 * @method AbstractColumn tinyBinary($column)
45
 * @method AbstractColumn longBinary($column)
46
 */
47
abstract class AbstractTable extends TableState implements TableInterface, LoggerAwareInterface
48
{
49
    /**
50
     * Some operation better to be logged.
51
     */
52
    use LoggerTrait;
53
54
    /**
55
     * Indication that table is exists and current schema is fetched from database.
56
     *
57
     * @var bool
58
     */
59
    private $exists = false;
60
61
    /**
62
     * Database specific tablePrefix. Required for table renames.
63
     *
64
     * @var string
65
     */
66
    private $prefix = '';
67
68
    /**
69
     * We have to remember original schema state to create set of diff based commands.
70
     *
71
     * @invisible
72
     * @var TableState
73
     */
74
    protected $initial = null;
75
76
    /**
77
     * Compares current and original states.
78
     *
79
     * @invisible
80
     * @var Comparator
81
     */
82
    protected $comparator = null;
83
84
    /**
85
     * @invisible
86
     * @var Driver
87
     */
88
    protected $driver = null;
89
90
    /**
91
     * Executes table operations.
92
     *
93
     * @var AbstractCommander
94
     */
95
    protected $commander = null;
96
97
    /**
98
     * @param Driver            $driver Parent driver.
99
     * @param AbstractCommander $commander
100
     * @param string            $name   Table name, must include table prefix.
101
     * @param string            $prefix Database specific table prefix.
102
     */
103
    public function __construct(Driver $driver, AbstractCommander $commander, $name, $prefix)
104
    {
105
        parent::__construct($name);
106
107
        $this->driver = $driver;
108
        $this->commander = $commander;
109
110
        $this->prefix = $prefix;
111
112
        //Locking down initial table state
113
        $this->initial = new TableState($name);
114
115
        //Needed to compare schemas
116
        $this->comparator = new Comparator($this->initial, $this);
117
118
        if (!$this->driver->hasTable($this->getName())) {
119
            //There is no need to load table schema when table does not exist
120
            return;
121
        }
122
123
        //Loading table information
124
        $this->loadColumns()->loadIndexes()->loadReferences();
125
126
        //Syncing schemas
127
        $this->initial->syncSchema($this);
128
129
        $this->exists = true;
130
    }
131
132
    /**
133
     * Get associated table driver.
134
     *
135
     * @return Driver
136
     */
137
    public function driver()
138
    {
139
        return $this->driver;
140
    }
141
142
    /**
143
     * Get table comparator.
144
     *
145
     * @return Comparator
146
     */
147
    public function comparator()
148
    {
149
        return $this->comparator;
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function exists()
156
    {
157
        return $this->exists;
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     *
163
     * Automatically forces prefix value.
164
     */
165
    public function setName($name)
166
    {
167
        parent::setName($this->prefix . $name);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     *
173
     * @param bool $quoted Quote name.
174
     */
175
    public function getName($quoted = false)
176
    {
177
        if (!$quoted) {
178
            return parent::getName();
179
        }
180
181
        return $this->driver->identifier(parent::getName());
182
    }
183
184
    /**
185
     * Return database specific table prefix.
186
     *
187
     * @return string
188
     */
189
    public function getPrefix()
190
    {
191
        return $this->prefix;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function getDependencies()
198
    {
199
        $tables = [];
200
        foreach ($this->getForeigns() as $foreign) {
201
            $tables[] = substr($foreign->getForeignTable(), strlen($this->prefix));
202
        }
203
204
        return $tables;
205
    }
206
207
    /**
208
     * Set table primary keys. Operation can only be applied for newly created tables. Now every
209
     * database might support compound indexes.
210
     *
211
     * @param array $columns
212
     * @return $this
213
     * @throws SchemaException
214
     */
215
    public function setPrimaryKeys(array $columns)
216
    {
217
        if ($this->exists() && $this->getPrimaryKeys() != $columns) {
218
            throw new SchemaException("Unable to change primary keys for already exists table.");
219
        }
220
221
        parent::setPrimaryKeys($columns);
222
223
        return $this;
224
    }
225
226
    /**
227
     * Get/create instance of AbstractColumn associated with current table.
228
     *
229
     * Examples:
230
     * $table->column('name')->string();
231
     *
232
     * @param string $name
233
     * @return AbstractColumn
234
     */
235
    public function column($name)
236
    {
237
        if (!empty($column = $this->findColumn($name))) {
238
            return $column->declared(true);
239
        }
240
241
        $column = $this->columnSchema($name)->declared(true);
242
243
        //Registering (without adding to initial schema)
244
        return parent::registerColumn($column);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (registerColumn() instead of column()). Are you sure this is correct? If so, you might want to change this to $this->registerColumn().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
245
    }
246
247
    /**
248
     * Get/create instance of AbstractIndex associated with current table based on list of forming
249
     * column names.
250
     *
251
     * Example:
252
     * $table->index('key');
253
     * $table->index('key', 'key2');
254
     * $table->index(['key', 'key2']);
255
     *
256
     * @param mixed $columns   Column name, or array of columns.
257
     * @param bool  $forceType Force index in non-unique state.
258
     * @return AbstractIndex
259
     */
260
    public function index($columns, $forceType = true)
0 ignored issues
show
Unused Code introduced by
The parameter $forceType is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
261
    {
262
        $columns = is_array($columns) ? $columns : func_get_args();
263
        if (!empty($index = $this->findIndex($columns))) {
264
            return $index->declared(true);
265
        }
266
267
        $index = $this->indexSchema(null)->declared(true);
268
        $index->columns($columns)->unique(false);
269
270
        return parent::registerIndex($index);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (registerIndex() instead of index()). Are you sure this is correct? If so, you might want to change this to $this->registerIndex().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
271
    }
272
273
    /**
274
     * Get/create instance of AbstractIndex associated with current table based on list of forming
275
     * column names. Index type must be forced as UNIQUE.
276
     *
277
     * Example:
278
     * $table->unique('key');
279
     * $table->unique('key', 'key2');
280
     * $table->unique(['key', 'key2']);
281
     *
282
     * @param mixed $columns Column name, or array of columns.
283
     * @return AbstractColumn|null
284
     */
285
    public function unique($columns)
286
    {
287
        $columns = is_array($columns) ? $columns : func_get_args();
288
289
        return $this->index($columns)->unique(true);
290
    }
291
292
    /**
293
     * Get/create instance of AbstractReference associated with current table based on local column
294
     * name.
295
     *
296
     * @param string $column Column name.
297
     * @return AbstractReference|null
298
     */
299
    public function foreign($column)
300
    {
301
        if (!empty($foreign = $this->findForeign($column))) {
302
            return $foreign->declared(true);
303
        }
304
305
        $foreign = $this->referenceSchema(null)->declared(true);
306
        $foreign->column($column);
307
308
        return parent::registerReference($foreign);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (registerReference() instead of foreign()). Are you sure this is correct? If so, you might want to change this to $this->registerReference().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
309
    }
310
311
    /**
312
     * Rename column (only if column exists).
313
     *
314
     * @param string $column
315
     * @param string $name New column name.
316
     * @return $this
317
     */
318
    public function renameColumn($column, $name)
319
    {
320
        if (empty($column = $this->findColumn($column))) {
321
            return $this;
322
        }
323
324
        //Renaming automatically declares column
325
        $column->declared(true)->setName($name);
326
327
        return $this;
328
    }
329
330
    /**
331
     * Rename index (only if index exists).
332
     *
333
     * @param array  $columns Index forming columns.
334
     * @param string $name    New index name.
335
     * @return $this
336
     */
337
    public function renameIndex(array $columns, $name)
338
    {
339
        if (empty($index = $this->findIndex($columns))) {
340
            return $this;
341
        }
342
343
        //Renaming automatically declares index
344
        $index->declared(true)->setName($name);
345
346
        return $this;
347
    }
348
349
    /**
350
     * Drop column by it's name.
351
     *
352
     * @param string $column
353
     * @return $this
354
     */
355
    public function dropColumn($column)
356
    {
357
        if (!empty($column = $this->findColumn($column))) {
358
            $this->forgetColumn($column);
359
            $this->removeDependent($column);
360
        }
361
362
        return $this;
363
    }
364
365
    /**
366
     * Drop index by it's forming columns.
367
     *
368
     * @param array $columns
369
     * @return $this
370
     */
371
    public function dropIndex(array $columns)
372
    {
373
        if (!empty($index = $this->findIndex($columns))) {
374
            $this->forgetIndex($index);
375
        }
376
377
        return $this;
378
    }
379
380
    /**
381
     * Drop foreign key by it's name.
382
     *
383
     * @param string $column
384
     * @return $this
385
     */
386
    public function dropForeign($column)
387
    {
388
        if (!empty($foreign = $this->findForeign($column))) {
389
            $this->forgetForeign($foreign);
390
        }
391
392
        return $this;
393
    }
394
395
    /**
396
     * Shortcut for column() method.
397
     *
398
     * @param string $column
399
     * @return AbstractColumn
400
     */
401
    public function __get($column)
402
    {
403
        return $this->column($column);
404
    }
405
406
    /**
407
     * Column creation/altering shortcut, call chain is identical to:
408
     * AbstractTable->column($name)->$type($arguments)
409
     *
410
     * Example:
411
     * $table->string("name");
412
     * $table->text("some_column");
413
     *
414
     * @param string $type
415
     * @param array  $arguments Type specific parameters.
416
     * @return AbstractColumn
417
     */
418
    public function __call($type, array $arguments)
419
    {
420
        return call_user_func_array(
421
            [$this->column($arguments[0]), $type],
422
            array_slice($arguments, 1)
423
        );
424
    }
425
426
    /**
427
     * Declare every existed element. Method has to be called if table modification applied to
428
     * existed table to prevent dropping of existed elements.
429
     *
430
     * @return $this
431
     */
432
    public function declareExisted()
433
    {
434
        foreach ($this->getColumns() as $column) {
435
            $column->declared(true);
436
        }
437
438
        foreach ($this->getIndexes() as $index) {
439
            $index->declared(true);
440
        }
441
442
        foreach ($this->getForeigns() as $foreign) {
443
            $foreign->declared(true);
444
        }
445
446
        return $this;
447
    }
448
449
    /**
450
     * Save table schema including every column, index, foreign key creation/altering. If table does
451
     * not exist it must be created.
452
     *
453
     * @param bool $forgetColumns  Drop all non declared columns.
454
     * @param bool $forgetIndexes  Drop all non declared indexes.
455
     * @param bool $forgetForeigns Drop all non declared foreign keys.
456
     */
457
    public function save($forgetColumns = true, $forgetIndexes = true, $forgetForeigns = true)
458
    {
459
        if (!$this->exists()) {
460
            $this->createSchema();
461
        } else {
462
            //Let's remove from schema elements which wasn't declared
463
            $this->forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns);
464
465
            if ($this->hasChanges()) {
466
                $this->synchroniseSchema();
467
            }
468
        }
469
470
        //Syncing internal states
471
        $this->initial->syncSchema($this);
472
        $this->exists = true;
473
    }
474
475
    /**
476
     * Drop table schema in database. This operation must be applied immediately.
477
     */
478
    public function drop()
479
    {
480
        $this->forgetElements();
481
482
        //Re-syncing initial state
483
        $this->initial->syncSchema($this->forgetElements());
484
485
        if ($this->exists()) {
486
            $this->commander->dropTable($this->getName());
487
        }
488
489
        $this->exists = false;
490
    }
491
492
    /**
493
     * @return AbstractColumn|string
494
     */
495
    public function __toString()
496
    {
497
        return $this->getName();
498
    }
499
500
    /**
501
     * @return object
502
     */
503
    public function __debugInfo()
504
    {
505
        return (object)[
506
            'name'        => $this->getName(),
507
            'primaryKeys' => $this->getPrimaryKeys(),
508
            'columns'     => array_values($this->getColumns()),
509
            'indexes'     => array_values($this->getIndexes()),
510
            'references'  => array_values($this->getForeigns())
511
        ];
512
    }
513
514
    /**
515
     * Create table.
516
     */
517
    protected function createSchema()
518
    {
519
        $this->logger()->debug("Creating new table {table}.", ['table' => $this->getName(true)]);
520
521
        $this->commander->createTable($this);
522
    }
523
524
    /**
525
     * Execute schema update.
526
     */
527
    protected function synchroniseSchema()
528
    {
529
        if ($this->getName() != $this->initial->getName()) {
530
            //Executing renaming
531
            $this->commander->renameTable($this->initial->getName(), $this->getName());
532
        }
533
534
        //Some data has to be dropped before column updates
535
        $this->dropForeigns()->dropIndexes();
536
537
        //Generate update flow
538
        $this->synchroniseColumns()->synchroniseIndexes()->synchroniseForeigns();
539
    }
540
541
    /**
542
     * Synchronise columns.
543
     *
544
     * @return $this
545
     */
546
    protected function synchroniseColumns()
547
    {
548
        foreach ($this->comparator->droppedColumns() as $column) {
549
            $this->logger()->debug("Dropping column [{statement}] from table {table}.", [
550
                'statement' => $column->sqlStatement(),
551
                'table'     => $this->getName(true)
552
            ]);
553
554
            $this->commander->dropColumn($this, $column);
555
        }
556
557
        foreach ($this->comparator->addedColumns() as $column) {
558
            $this->logger()->debug("Adding column [{statement}] into table {table}.", [
559
                'statement' => $column->sqlStatement(),
560
                'table'     => $this->getName(true)
561
            ]);
562
563
            $this->commander->addColumn($this, $column);
564
        }
565
566
        foreach ($this->comparator->alteredColumns() as $pair) {
567
            /**
568
             * @var AbstractColumn $initial
569
             * @var AbstractColumn $current
570
             */
571
            list($current, $initial) = $pair;
572
573
            $this->logger()->debug("Altering column [{statement}] to [{new}] in table {table}.", [
574
                'statement' => $initial->sqlStatement(),
575
                'new'       => $current->sqlStatement(),
576
                'table'     => $this->getName(true)
577
            ]);
578
579
            $this->commander->alterColumn($this, $initial, $current);
580
        }
581
582
        return $this;
583
    }
584
585
    /**
586
     * Drop needed indexes.
587
     *
588
     * @return $this
589
     */
590 View Code Duplication
    protected function dropIndexes()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
591
    {
592
        foreach ($this->comparator->droppedIndexes() as $index) {
593
            $this->logger()->debug("Dropping index [{statement}] from table {table}.", [
594
                'statement' => $index->sqlStatement(),
595
                'table'     => $this->getName(true)
596
            ]);
597
598
            $this->commander->dropIndex($this, $index);
599
        }
600
601
        return $this;
602
    }
603
604
    /**
605
     * Synchronise indexes.
606
     *
607
     * @return $this
608
     */
609 View Code Duplication
    protected function synchroniseIndexes()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
610
    {
611
        foreach ($this->comparator->addedIndexes() as $index) {
612
            $this->logger()->debug("Adding index [{statement}] into table {table}.", [
613
                'statement' => $index->sqlStatement(),
614
                'table'     => $this->getName(true)
615
            ]);
616
617
            $this->commander->addIndex($this, $index);
618
        }
619
620
        foreach ($this->comparator->alteredIndexes() as $pair) {
621
            /**
622
             * @var AbstractIndex $initial
623
             * @var AbstractIndex $current
624
             */
625
            list($current, $initial) = $pair;
626
627
            $this->logger()->debug("Altering index [{statement}] to [{new}] in table {table}.", [
628
                'statement' => $initial->sqlStatement(),
629
                'new'       => $current->sqlStatement(),
630
                'table'     => $this->getName(true)
631
            ]);
632
633
            $this->commander->alterIndex($this, $initial, $current);
634
        }
635
636
        return $this;
637
    }
638
639
    /**
640
     * Drop needed foreign keys.
641
     *
642
     * @return $this
643
     */
644 View Code Duplication
    protected function dropForeigns()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
645
    {
646
        foreach ($this->comparator->droppedForeigns() as $foreign) {
647
            $this->logger()->debug("Dropping foreign key [{statement}] from table {table}.", [
648
                'statement' => $foreign->sqlStatement(),
649
                'table'     => $this->getName(true)
650
            ]);
651
652
            $this->commander->dropForeign($this, $foreign);
653
        }
654
655
        return $this;
656
    }
657
658
    /**
659
     * Synchronise foreign keys.
660
     *
661
     * @return $this
662
     */
663 View Code Duplication
    protected function synchroniseForeigns()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
664
    {
665
        foreach ($this->comparator->addedForeigns() as $foreign) {
666
            $this->logger()->debug("Adding foreign key [{statement}] into table {table}.", [
667
                'statement' => $foreign->sqlStatement(),
668
                'table'     => $this->getName(true)
669
            ]);
670
671
            $this->commander->addForeign($this, $foreign);
672
        }
673
674
        foreach ($this->comparator->alteredForeigns() as $pair) {
675
            /**
676
             * @var AbstractReference $initial
677
             * @var AbstractReference $current
678
             */
679
            list($current, $initial) = $pair;
680
681
            $this->logger()->debug("Altering foreign key [{statement}] to [{new}] in {table}.", [
682
                'statement' => $initial->sqlStatement(),
683
                'table'     => $this->getName(true)
684
            ]);
685
686
            $this->commander->alterForeign($this, $initial, $current);
687
        }
688
689
        return $this;
690
    }
691
692
    /**
693
     * Driver specific column schema.
694
     *
695
     * @param string $name
696
     * @param mixed  $schema
697
     * @return AbstractColumn
698
     */
699
    abstract protected function columnSchema($name, $schema = null);
700
701
    /**
702
     * Driver specific index schema.
703
     *
704
     * @param string $name
705
     * @param mixed  $schema
706
     * @return AbstractIndex
707
     */
708
    abstract protected function indexSchema($name, $schema = null);
709
710
    /**
711
     * Driver specific reference schema.
712
     *
713
     * @param string $name
714
     * @param mixed  $schema
715
     * @return AbstractReference
716
     */
717
    abstract protected function referenceSchema($name, $schema = null);
718
719
720
    /**
721
     * Must load table columns.
722
     *
723
     * @see registerColumn()
724
     * @return self
725
     */
726
    abstract protected function loadColumns();
727
728
    /**
729
     * Must load table indexes.
730
     *
731
     * @see registerIndex()
732
     * @return self
733
     */
734
    abstract protected function loadIndexes();
735
736
    /**
737
     * Must load table references.
738
     *
739
     * @see registerReference()
740
     * @return self
741
     */
742
    abstract protected function loadReferences();
743
744
    /**
745
     * Check if table schema has been modified. Attention, you have to execute dropUndeclared first
746
     * to get valid results.
747
     *
748
     * @return bool
749
     */
750
    protected function hasChanges()
751
    {
752
        return $this->comparator->hasChanges();
753
    }
754
755
    /**
756
     * Calculate difference (removed columns, indexes and foreign keys).
757
     *
758
     * @param bool $forgetColumns
759
     * @param bool $forgetIndexes
760
     * @param bool $forgetForeigns
761
     */
762
    protected function forgetUndeclared($forgetColumns, $forgetIndexes, $forgetForeigns)
763
    {
764
        //We don't need to worry about changed or created columns, indexes and foreign keys here
765
        //as it already handled, we only have to drop columns which were not listed in schema
766
767
        foreach ($this->getColumns() as $column) {
768
            if ($forgetColumns && !$column->isDeclared()) {
769
                $this->forgetColumn($column);
770
                $this->removeDependent($column);
771
            }
772
        }
773
774
        foreach ($this->getIndexes() as $index) {
775
            if ($forgetIndexes && !$index->isDeclared()) {
776
                $this->forgetIndex($index);
777
            }
778
        }
779
780
        foreach ($this->getForeigns() as $foreign) {
781
            if ($forgetForeigns && !$foreign->isDeclared()) {
782
                $this->forgetForeign($foreign);
783
            }
784
        }
785
    }
786
787
    /**
788
     * Remove dependent indexes and foreign keys.
789
     *
790
     * @param ColumnInterface $column
791
     */
792
    private function removeDependent(ColumnInterface $column)
793
    {
794
        if ($this->hasForeign($column->getName())) {
795
            $this->forgetForeign($this->foreign($column->getName()));
796
        }
797
798
        foreach ($this->getIndexes() as $index) {
799
            if (in_array($column->getName(), $index->getColumns())) {
800
                //Dropping related indexes
801
                $this->forgetIndex($index);
802
            }
803
        }
804
    }
805
806
    /**
807
     * Forget all elements.
808
     *
809
     * @return $this
810
     */
811
    private function forgetElements()
812
    {
813
        foreach ($this->getColumns() as $column) {
814
            $this->forgetColumn($column);
815
        }
816
817
        foreach ($this->getIndexes() as $index) {
818
            $this->forgetIndex($index);
819
        }
820
821
        foreach ($this->getForeigns() as $foreign) {
822
            $this->forgetForeign($foreign);
823
        }
824
825
        return $this;
826
    }
827
}