Completed
Push — master ( 7137c5...e7caf5 )
by Neomerx
01:51
created

MigrationTrait::dropTableIfExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.9666
cc 2
nc 2
nop 1
crap 2
1
<?php namespace Limoncello\Data\Migrations;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Closure;
20
use Doctrine\DBAL\Connection;
21
use Doctrine\DBAL\DBALException;
22
use Doctrine\DBAL\Schema\AbstractSchemaManager;
23
use Doctrine\DBAL\Schema\Table;
24
use Doctrine\DBAL\Types\Type;
25
use Limoncello\Contracts\Data\MigrationInterface;
26
use Limoncello\Contracts\Data\ModelSchemaInfoInterface;
27
use Limoncello\Contracts\Data\RelationshipTypes;
28
use Limoncello\Contracts\Data\TimestampFields;
29
use Limoncello\Data\Contracts\MigrationContextInterface;
30
use Psr\Container\ContainerInterface;
31
32
/**
33
 * @package Limoncello\Data
34
 */
35
trait MigrationTrait
36
{
37
    /**
38
     * @var ContainerInterface
39
     */
40
    private $container;
41
42
    /**
43
     * @var array
44
     */
45
    private $enumerations = [];
46
47
    /**
48
     * @inheritdoc
49
     */
50 6
    public function init(ContainerInterface $container): MigrationInterface
51
    {
52 6
        $this->container = $container;
53
54
        /** @var MigrationInterface $self */
55 6
        $self = $this;
56
57 6
        return $self;
58
    }
59
60
    /**
61
     * @return ContainerInterface
62
     */
63 6
    protected function getContainer(): ContainerInterface
64
    {
65 6
        return $this->container;
66
    }
67
68
    /**
69
     * @return Connection
70
     */
71 6
    protected function getConnection(): Connection
72
    {
73 6
        assert($this->getContainer()->has(Connection::class) === true);
74
75 6
        return $this->getContainer()->get(Connection::class);
76
    }
77
78
    /**
79
     * @return ModelSchemaInfoInterface
80
     */
81 2
    protected function getModelSchemas(): ModelSchemaInfoInterface
82
    {
83 2
        assert($this->getContainer()->has(ModelSchemaInfoInterface::class) === true);
84
85 2
        return $this->getContainer()->get(ModelSchemaInfoInterface::class);
86
    }
87
88
    /**
89
     * @return AbstractSchemaManager
90
     */
91 2
    protected function getSchemaManager(): AbstractSchemaManager
92
    {
93 2
        return $this->getConnection()->getSchemaManager();
94
    }
95
96
    /**
97
     * @param string    $modelClass
98
     * @param Closure[] $expressions
99
     *
100
     * @return Table
101
     *
102
     * @throws DBALException
103
     */
104 2
    protected function createTable(string $modelClass, array $expressions = []): Table
105
    {
106 2
        $context   = new MigrationContext($modelClass, $this->getModelSchemas());
107 2
        $tableName = $this->getModelSchemas()->getTable($modelClass);
108 2
        $table     = new Table($tableName);
109 2
        foreach ($expressions as $expression) {
110
            /** @var Closure $expression */
111 2
            $expression($table, $context);
112
        }
113
114 2
        $this->getSchemaManager()->dropAndCreateTable($table);
115
116 2
        return $table;
117
    }
118
119
    /**
120
     * @param string $modelClass
121
     *
122
     * @return void
123
     */
124 1
    protected function dropTableIfExists(string $modelClass): void
125
    {
126 1
        $tableName     = $this->getModelSchemas()->getTable($modelClass);
127 1
        $schemaManager = $this->getSchemaManager();
128
129 1
        if ($schemaManager->tablesExist([$tableName]) === true) {
130 1
            $schemaManager->dropTable($tableName);
131
        }
132
    }
133
134
    /**
135
     * @param string $name
136
     * @param array  $values
137
     *
138
     * @return void
139
     *
140
     * @throws DBALException
141
     */
142 2
    protected function createEnum(string $name, array $values): void
143
    {
144 2
        assert(empty($name) === false);
145
146
        // check all values are strings
147 2
        assert(
148
            call_user_func(function () use ($values): bool {
149 2
                $allAreStrings = true;
150 2
                foreach ($values as $value) {
151 2
                    $allAreStrings = $allAreStrings && is_string($value);
152
                }
153
154 2
                return $allAreStrings;
155 2
            }) === true,
156 2
            'All enum values should be strings.'
157
        );
158
159 2
        assert(array_key_exists($name, $this->enumerations) === false, "Enum name `$name` has already been used.");
160 2
        $this->enumerations[$name] = $values;
161
162 2
        $connection = $this->getConnection();
163 2
        if ($connection->getDriver()->getName() === 'pdo_pgsql') {
164 1
            $valueList = implode("', '", $values);
165 1
            $sql       = "CREATE TYPE $name AS ENUM ('$valueList');";
166 1
            $connection->exec($sql);
167
        }
168
    }
169
170
    /**
171
     * @param string $name
172
     *
173
     * @return void
174
     *
175
     * @throws DBALException
176
     */
177 1
    protected function dropEnumIfExists(string $name): void
178
    {
179 1
        unset($this->enumerations[$name]);
180
181 1
        $connection = $this->getConnection();
182 1
        if ($connection->getDriver()->getName() === 'pdo_pgsql') {
183 1
            $name = $connection->quoteIdentifier($name);
184 1
            $sql  = "DROP TYPE IF EXISTS $name;";
185 1
            $connection->exec($sql);
186
        }
187
    }
188
189
    /**
190
     * @param string $columnName
191
     * @param string $enumName
192
     * @param bool   $notNullable
193
     *
194
     * @return Closure
195
     *
196
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
197
     * @SuppressWarnings(PHPMD.ElseExpression)
198
     * @SuppressWarnings(PHPMD.StaticAccess)
199
     */
200 2
    protected function useEnum(string $columnName, string $enumName, bool $notNullable = true): Closure
201
    {
202 2
        if ($this->getConnection()->getDriver()->getName() === 'pdo_pgsql') {
203
            return function (Table $table) use ($columnName, $enumName): void {
204 1
                $typeName = RawNameType::TYPE_NAME;
205 1
                Type::hasType($typeName) === true ?: Type::addType($typeName, RawNameType::class);
206
                $table
207 1
                    ->addColumn($columnName, $typeName)
208 1
                    ->setCustomSchemaOption($typeName, $enumName);
209 1
            };
210
        } else {
211 1
            $enumValues = $this->enumerations[$enumName];
212
213
            return function (Table $table) use ($columnName, $enumValues, $notNullable) {
214 1
                Type::hasType(EnumType::TYPE_NAME) === true ?: Type::addType(EnumType::TYPE_NAME, EnumType::class);
215
                $table
216 1
                    ->addColumn($columnName, EnumType::TYPE_NAME)
217 1
                    ->setCustomSchemaOption(EnumType::TYPE_NAME, $enumValues)
218 1
                    ->setNotnull($notNullable);
219 1
            };
220
        }
221
    }
222
223
    /**
224
     * @param string $name
225
     *
226
     * @return Closure
227
     */
228 2
    protected function primaryInt(string $name): Closure
229
    {
230
        return function (Table $table) use ($name) {
231 2
            $table->addColumn($name, Type::INTEGER)->setAutoincrement(true)->setUnsigned(true)->setNotnull(true);
232 2
            $table->setPrimaryKey([$name]);
233 2
        };
234
    }
235
236
    /**
237
     * @param string $name
238
     *
239
     * @return Closure
240
     */
241 1
    protected function primaryString(string $name): Closure
242
    {
243
        return function (Table $table, MigrationContextInterface $context) use ($name) {
244 1
            $length = $context->getModelSchemas()->getAttributeLength($context->getModelClass(), $name);
245 1
            $table->addColumn($name, Type::STRING)->setLength($length)->setNotnull(true);
246 1
            $table->setPrimaryKey([$name]);
247 1
        };
248
    }
249
250
    /**
251
     * @param string   $name
252
     * @param null|int $default
253
     *
254
     * @return Closure
255
     */
256 1
    protected function unsignedInt(string $name, int $default = null): Closure
257
    {
258 1
        return $this->unsignedIntImpl($name, true, $default);
259
    }
260
261
    /**
262
     * @param string   $name
263
     * @param null|int $default
264
     *
265
     * @return Closure
266
     */
267 1
    protected function nullableUnsignedInt(string $name, int $default = null): Closure
268
    {
269 1
        return $this->unsignedIntImpl($name, false, $default);
270
    }
271
272
    /**
273
     * @param string $name
274
     *
275
     * @return Closure
276
     */
277 1
    protected function float(string $name): Closure
278
    {
279
        // precision and scale both seems to be ignored in Doctrine so not much sense to have them as inputs
280
281
        return function (Table $table) use ($name) {
282 1
            $table->addColumn($name, Type::FLOAT)->setNotnull(true);
283 1
        };
284
    }
285
286
    /**
287
     * @param string $name
288
     *
289
     * @return Closure
290
     */
291 1
    protected function string(string $name): Closure
292
    {
293
        return function (Table $table, MigrationContextInterface $context) use ($name) {
294 1
            $length = $context->getModelSchemas()->getAttributeLength($context->getModelClass(), $name);
295 1
            $table->addColumn($name, Type::STRING)->setLength($length)->setNotnull(true);
296 1
        };
297
    }
298
299
    /**
300
     * @param string $name
301
     *
302
     * @return Closure
303
     */
304 1
    protected function nullableString(string $name): Closure
305
    {
306
        return function (Table $table, MigrationContextInterface $context) use ($name) {
307 1
            $length = $context->getModelSchemas()->getAttributeLength($context->getModelClass(), $name);
308 1
            $table->addColumn($name, Type::STRING)->setLength($length)->setNotnull(false);
309 1
        };
310
    }
311
312
    /**
313
     * @param string $name
314
     *
315
     * @return Closure
316
     */
317 1
    protected function text(string $name): Closure
318
    {
319
        return function (Table $table) use ($name) {
320 1
            $table->addColumn($name, Type::TEXT)->setNotnull(true);
321 1
        };
322
    }
323
324
    /**
325
     * @param string $name
326
     *
327
     * @return Closure
328
     */
329 1
    protected function nullableText(string $name): Closure
330
    {
331
        return function (Table $table) use ($name) {
332 1
            $table->addColumn($name, Type::TEXT)->setNotnull(false);
333 1
        };
334
    }
335
336
    /**
337
     * @param string    $name
338
     * @param null|bool $default
339
     *
340
     * @return Closure
341
     */
342 1
    protected function bool(string $name, $default = null): Closure
343
    {
344
        return function (Table $table) use ($name, $default) {
345 1
            $column = $table->addColumn($name, Type::BOOLEAN)->setNotnull(true);
346 1
            if ($default !== null && is_bool($default) === true) {
347 1
                $column->setDefault($default);
348
            }
349 1
        };
350
    }
351
352
    /**
353
     * @param string $name
354
     * @param array  $values
355
     *
356
     * @return Closure
357
     *
358
     * @throws DBALException
359
     */
360 1
    protected function enum(string $name, array $values): Closure
361
    {
362 1
        $this->createEnum($name, $values);
363
364 1
        return $this->useEnum($name, $name, true);
365
    }
366
367
    /**
368
     * @param string $name
369
     * @param array  $values
370
     *
371
     * @return Closure
372
     *
373
     * @throws DBALException
374
     */
375 1
    protected function nullableEnum(string $name, array $values): Closure
376
    {
377 1
        $this->createEnum($name, $values);
378
379 1
        return $this->useEnum($name, $name, false);
380
    }
381
382
    /**
383
     * @return Closure
384
     */
385 1
    protected function timestamps(): Closure
386
    {
387
        return function (Table $table, MigrationContextInterface $context) {
388 1
            $modelClass = $context->getModelClass();
389
390 1
            $createdAt = TimestampFields::FIELD_CREATED_AT;
391 1
            $updatedAt = TimestampFields::FIELD_UPDATED_AT;
392 1
            $deletedAt = TimestampFields::FIELD_DELETED_AT;
393
394
            // a list of data columns and `nullable` flag
395 1
            $datesToAdd = [];
396 1
            if ($this->getModelSchemas()->hasAttributeType($modelClass, $createdAt) === true) {
397 1
                $datesToAdd[$createdAt] = true;
398
            }
399 1
            if ($this->getModelSchemas()->hasAttributeType($modelClass, $updatedAt) === true) {
400 1
                $datesToAdd[$updatedAt] = false;
401
            }
402 1
            if ($this->getModelSchemas()->hasAttributeType($modelClass, $deletedAt) === true) {
403 1
                $datesToAdd[$deletedAt] = false;
404
            }
405
406 1
            foreach ($datesToAdd as $column => $isNullable) {
407 1
                $table->addColumn($column, Type::DATETIME)->setNotnull($isNullable);
408
            }
409 1
        };
410
    }
411
412
    /**
413
     * @param string $name
414
     *
415
     * @return Closure
416
     */
417 1
    protected function datetime(string $name): Closure
418
    {
419
        return function (Table $table) use ($name) {
420 1
            $table->addColumn($name, Type::DATETIME)->setNotnull(true);
421 1
        };
422
    }
423
424
    /**
425
     * @param string $name
426
     *
427
     * @return Closure
428
     */
429 1
    protected function nullableDatetime(string $name): Closure
430
    {
431
        return function (Table $table) use ($name) {
432 1
            $table->addColumn($name, Type::DATETIME)->setNotnull(false);
433 1
        };
434
    }
435
436
    /**
437
     * @param string $name
438
     *
439
     * @return Closure
440
     */
441 1
    protected function date(string $name): Closure
442
    {
443
        return function (Table $table) use ($name) {
444 1
            $table->addColumn($name, Type::DATE)->setNotnull(true);
445 1
        };
446
    }
447
448
    /**
449
     * @param string $name
450
     *
451
     * @return Closure
452
     */
453 1
    protected function nullableDate(string $name): Closure
454
    {
455
        return function (Table $table) use ($name) {
456 1
            $table->addColumn($name, Type::DATE)->setNotnull(false);
457 1
        };
458
    }
459
460
    /**
461
     * @param string[] $names
462
     *
463
     * @return Closure
464
     */
465 1
    protected function unique(array $names): Closure
466
    {
467
        return function (Table $table) use ($names) {
468 1
            $table->addUniqueIndex($names);
469 1
        };
470
    }
471
472
    /**
473
     * @param string[] $names
474
     *
475
     * @return Closure
476
     */
477 1
    protected function searchable(array $names): Closure
478
    {
479
        return function (Table $table) use ($names) {
480 1
            $table->addIndex($names, null, ['fulltext']);
481 1
        };
482
    }
483
484
    /**
485
     * @param string $column
486
     * @param string $referredClass
487
     * @param bool   $cascadeDelete
488
     *
489
     * @return Closure
490
     *
491
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
492
     */
493 1
    protected function foreignRelationship(
494
        string $column,
495
        string $referredClass,
496
        bool $cascadeDelete = false
497
    ): Closure {
498
        return function (
499
            Table $table,
500
            MigrationContextInterface $context
501
        ) use (
502 1
            $column,
503 1
            $referredClass,
504 1
            $cascadeDelete
505
        ) {
506 1
            $tableName    = $this->getTableNameForClass($referredClass);
507 1
            $pkName       = $this->getModelSchemas()->getPrimaryKey($referredClass);
508 1
            $columnType   = $this->getModelSchemas()->getAttributeType($context->getModelClass(), $column);
509 1
            $columnLength = $columnType === Type::STRING ?
510 1
                $this->getModelSchemas()->getAttributeLength($context->getModelClass(), $column) : null;
511
512 1
            $closure = $this->foreignColumn($column, $tableName, $pkName, $columnType, $columnLength, $cascadeDelete);
513
514 1
            return $closure($table, $context);
515 1
        };
516
    }
517
518
    /**
519
     * @param string $column
520
     * @param string $referredClass
521
     * @param bool   $cascadeDelete
522
     *
523
     * @return Closure
524
     *
525
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
526
     */
527 1
    protected function nullableForeignRelationship(
528
        string $column,
529
        string $referredClass,
530
        bool $cascadeDelete = false
531
    ): Closure {
532
        return function (
533
            Table $table,
534
            MigrationContextInterface $context
535
        ) use (
536 1
            $column,
537 1
            $referredClass,
538 1
            $cascadeDelete
539
        ) {
540 1
            $tableName    = $this->getTableNameForClass($referredClass);
541 1
            $pkName       = $this->getModelSchemas()->getPrimaryKey($referredClass);
542 1
            $columnType   = $this->getModelSchemas()->getAttributeType($context->getModelClass(), $column);
543 1
            $columnLength = $columnType === Type::STRING ?
544 1
                $this->getModelSchemas()->getAttributeLength($context->getModelClass(), $column) : null;
545
546
            $closure = $this
547 1
                ->nullableForeignColumn($column, $tableName, $pkName, $columnType, $columnLength, $cascadeDelete);
548
549 1
            return $closure($table, $context);
550 1
        };
551
    }
552
553
    /** @noinspection PhpTooManyParametersInspection
554
     * @param string   $localKey
555
     * @param string   $foreignTable
556
     * @param string   $foreignKey
557
     * @param string   $type
558
     * @param int|null $length
559
     * @param bool     $cascadeDelete
560
     *
561
     * @return Closure
562
     *
563
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
564
     */
565 1
    protected function foreignColumn(
566
        string $localKey,
567
        string $foreignTable,
568
        string $foreignKey,
569
        string $type,
570
        ?int $length = null,
571
        bool $cascadeDelete = false
572
    ): Closure {
573 1
        return $this->foreignColumnImpl($localKey, $foreignTable, $foreignKey, $type, $length, true, $cascadeDelete);
574
    }
575
576
    /** @noinspection PhpTooManyParametersInspection
577
     * @param string   $localKey
578
     * @param string   $foreignTable
579
     * @param string   $foreignKey
580
     * @param string   $type
581
     * @param int|null $length
582
     * @param bool     $cascadeDelete
583
     *
584
     * @return Closure
585
     *
586
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
587
     */
588 1
    protected function nullableForeignColumn(
589
        string $localKey,
590
        string $foreignTable,
591
        string $foreignKey,
592
        string $type,
593
        ?int $length = null,
594
        bool $cascadeDelete = false
595
    ): Closure {
596 1
        return $this->foreignColumnImpl($localKey, $foreignTable, $foreignKey, $type, $length, false, $cascadeDelete);
597
    }
598
599
    /**
600
     * @param string $name
601
     * @param bool   $cascadeDelete
602
     *
603
     * @return Closure
604
     *
605
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
606
     */
607 1
    protected function nullableRelationship(string $name, bool $cascadeDelete = false): Closure
608
    {
609 1
        return $this->relationshipImpl($name, false, $cascadeDelete);
610
    }
611
612
    /**
613
     * @param string $name
614
     * @param bool   $cascadeDelete
615
     *
616
     * @return Closure
617
     *
618
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
619
     */
620 1
    protected function relationship(string $name, bool $cascadeDelete = false): Closure
621
    {
622 1
        return $this->relationshipImpl($name, true, $cascadeDelete);
623
    }
624
625
    /**
626
     * @param string $modelClass
627
     *
628
     * @return string
629
     */
630 1
    protected function getTableNameForClass(string $modelClass): string
631
    {
632 1
        assert(
633 1
            $this->getModelSchemas()->hasClass($modelClass),
634 1
            "Table name is not specified for model '$modelClass'."
635
        );
636
637 1
        $tableName = $this->getModelSchemas()->getTable($modelClass);
638
639 1
        return $tableName;
640
    }
641
642
    /**
643
     * @param string $name
644
     * @param mixed  $value
645
     *
646
     * @return Closure
647
     */
648 1
    protected function defaultValue(string $name, $value): Closure
649
    {
650
        return function (Table $table) use ($name, $value) {
651 1
            assert($table->hasColumn($name));
652 1
            $table->getColumn($name)->setDefault($value);
653 1
        };
654
    }
655
656
    /**
657
     * @param string     $name
658
     * @param bool       $notNullable
659
     * @param null|mixed $default
660
     *
661
     * @return Closure
662
     */
663 1
    private function unsignedIntImpl(string $name, bool $notNullable, $default = null): Closure
664
    {
665
        return function (Table $table) use ($name, $notNullable, $default) {
666 1
            $column = $table->addColumn($name, Type::INTEGER)->setUnsigned(true)->setNotnull($notNullable);
667 1
            $default === null ?: $column->setDefault($default);
668 1
        };
669
    }
670
671
    /** @noinspection PhpTooManyParametersInspection
672
     * @param string   $localKey
673
     * @param string   $foreignTable
674
     * @param string   $foreignKey
675
     * @param string   $type
676
     * @param int|null $length
677
     * @param bool     $notNullable
678
     * @param bool     $cascadeDelete
679
     *
680
     * @return Closure
681
     */
682 1
    private function foreignColumnImpl(
683
        string $localKey,
684
        string $foreignTable,
685
        string $foreignKey,
686
        string $type,
687
        ?int $length,
688
        bool $notNullable,
689
        bool $cascadeDelete
690
    ): Closure {
691
        return function (Table $table) use (
692 1
            $localKey,
693 1
            $foreignTable,
694 1
            $foreignKey,
695 1
            $notNullable,
696 1
            $cascadeDelete,
697 1
            $type,
698 1
            $length
699
        ) {
700 1
            $options = $cascadeDelete === true ? ['onDelete' => 'CASCADE'] : [];
701 1
            $column  = $table->addColumn($localKey, $type)->setNotnull($notNullable);
702 1
            $length === null ? $column->setUnsigned(true) : $column->setLength($length);
703 1
            $table->addForeignKeyConstraint($foreignTable, [$localKey], [$foreignKey], $options);
704 1
        };
705
    }
706
707
    /**
708
     * @param string $name
709
     * @param bool   $notNullable
710
     * @param bool   $cascadeDelete
711
     *
712
     * @return Closure
713
     */
714 1
    private function relationshipImpl(string $name, bool $notNullable, bool $cascadeDelete): Closure
715
    {
716
        return function (
717
            Table $table,
718
            MigrationContextInterface $context
719
        ) use (
720 1
            $name,
721 1
            $notNullable,
722 1
            $cascadeDelete
723
        ) {
724 1
            $modelClass = $context->getModelClass();
725
726 1
            assert(
727 1
                $this->getModelSchemas()->hasRelationship($modelClass, $name),
728 1
                "Relationship `$name` not found for model `$modelClass`."
729
            );
730 1
            assert(
731 1
                $this->getModelSchemas()->getRelationshipType($modelClass, $name) === RelationshipTypes::BELONGS_TO,
732 1
                "Relationship `$name` for model `$modelClass` must be `belongsTo`."
733
            );
734
735 1
            $localKey     = $this->getModelSchemas()->getForeignKey($modelClass, $name);
736 1
            $columnType   = $this->getModelSchemas()->getAttributeType($modelClass, $localKey);
737 1
            $columnLength = $columnType === Type::STRING ?
738 1
                $this->getModelSchemas()->getAttributeLength($modelClass, $localKey) : null;
739
740 1
            $otherModelClass = $this->getModelSchemas()->getReverseModelClass($modelClass, $name);
741 1
            $foreignTable    = $this->getModelSchemas()->getTable($otherModelClass);
742 1
            $foreignKey      = $this->getModelSchemas()->getPrimaryKey($otherModelClass);
743
744 1
            $fkClosure = $this->foreignColumnImpl(
745 1
                $localKey,
746 1
                $foreignTable,
747 1
                $foreignKey,
748 1
                $columnType,
749 1
                $columnLength,
750 1
                $notNullable,
751 1
                $cascadeDelete
752
            );
753
754 1
            return $fkClosure($table);
755 1
        };
756
    }
757
}
758