Failed Conditions
Push — master ( 656579...2742cd )
by Marco
11:55
created

Table   F

Complexity

Total Complexity 99

Size/Duplication

Total Lines 829
Duplicated Lines 6.63 %

Test Coverage

Coverage 96.19%

Importance

Changes 0
Metric Value
wmc 99
dl 55
loc 829
ccs 227
cts 236
cp 0.9619
rs 1.263
c 0
b 0
f 0

43 Methods

Rating   Name   Duplication   Size   Complexity  
A addIndex() 9 9 2
A addColumn() 0 7 1
A hasIndex() 0 5 1
B _addForeignKeyConstraint() 0 33 4
A setPrimaryKey() 0 10 3
A hasColumn() 0 5 1
A getIndexes() 0 3 1
A hasPrimaryKey() 0 3 2
A getOption() 0 3 1
A _getMaxIdentifierLength() 0 6 2
A hasForeignKey() 0 5 1
A getOptions() 0 3 1
A visit() 0 14 4
A columnsAreIndexed() 0 10 3
A normalizeIdentifier() 0 3 1
A getColumns() 0 8 2
A dropPrimaryKey() 0 4 1
A filterColumns() 0 5 1
A addOption() 0 5 1
A getForeignKeyColumns() 0 8 2
A dropColumn() 0 6 1
A getForeignKeys() 0 3 1
A changeColumn() 0 6 1
A addUnnamedForeignKeyConstraint() 0 3 1
A hasOption() 0 3 1
A addUniqueIndex() 9 9 2
A addForeignKeyConstraint() 0 5 2
A setSchemaConfig() 0 3 1
A __clone() 0 11 4
A getPrimaryKey() 0 7 2
A getPrimaryKeyColumns() 0 6 2
A getForeignKey() 8 8 2
B __construct() 0 21 5
B renameIndex() 0 32 6
B addNamedForeignKeyConstraint() 0 22 6
A removeForeignKey() 8 8 2
D _addIndex() 0 29 10
A renameColumn() 0 5 1
A _addColumn() 0 10 2
A getColumn() 0 8 2
A getIndex() 8 8 2
B _createIndex() 0 17 6
A dropIndex() 7 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Table often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Table, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Types\Type;
23
use Doctrine\DBAL\Schema\Visitor\Visitor;
24
use Doctrine\DBAL\DBALException;
25
26
/**
27
 * Object Representation of a table.
28
 *
29
 * @link   www.doctrine-project.org
30
 * @since  2.0
31
 * @author Benjamin Eberlei <[email protected]>
32
 */
33
class Table extends AbstractAsset
34
{
35
    /**
36
     * @var string
37
     */
38
    protected $_name = null;
39
40
    /**
41
     * @var Column[]
42
     */
43
    protected $_columns = [];
44
45
    /**
46
     * @var Index[]
47
     */
48
    private $implicitIndexes = [];
49
50
    /**
51
     * @var Index[]
52
     */
53
    protected $_indexes = [];
54
55
    /**
56
     * @var string
57
     */
58
    protected $_primaryKeyName = false;
59
60
    /**
61
     * @var ForeignKeyConstraint[]
62
     */
63
    protected $_fkConstraints = [];
64
65
    /**
66
     * @var array
67
     */
68
    protected $_options = [];
69
70
    /**
71
     * @var SchemaConfig
72
     */
73
    protected $_schemaConfig = null;
74
75
    /**
76
     * @param string                 $tableName
77
     * @param Column[]               $columns
78
     * @param Index[]                $indexes
79
     * @param ForeignKeyConstraint[] $fkConstraints
80
     * @param integer                $idGeneratorType
81
     * @param array                  $options
82
     *
83
     * @throws DBALException
84
     */
85 809
    public function __construct($tableName, array $columns=[], array $indexes=[], array $fkConstraints=[], $idGeneratorType = 0, array $options=[])
0 ignored issues
show
Unused Code introduced by
The parameter $idGeneratorType is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

85
    public function __construct($tableName, array $columns=[], array $indexes=[], array $fkConstraints=[], /** @scrutinizer ignore-unused */ $idGeneratorType = 0, array $options=[])

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

Loading history...
86
    {
87 809
        if (strlen($tableName) == 0) {
88 1
            throw DBALException::invalidTableName($tableName);
89
        }
90
91 808
        $this->_setName($tableName);
92
93 808
        foreach ($columns as $column) {
94 72
            $this->_addColumn($column);
95
        }
96
97 807
        foreach ($indexes as $idx) {
98 30
            $this->_addIndex($idx);
99
        }
100
101 805
        foreach ($fkConstraints as $constraint) {
102 6
            $this->_addForeignKeyConstraint($constraint);
103
        }
104
105 805
        $this->_options = $options;
106 805
    }
107
108
    /**
109
     * @param SchemaConfig $schemaConfig
110
     *
111
     * @return void
112
     */
113 61
    public function setSchemaConfig(SchemaConfig $schemaConfig)
114
    {
115 61
        $this->_schemaConfig = $schemaConfig;
116 61
    }
117
118
    /**
119
     * @return integer
120
     */
121 152
    protected function _getMaxIdentifierLength()
122
    {
123 152
        if ($this->_schemaConfig instanceof SchemaConfig) {
124 8
            return $this->_schemaConfig->getMaxIdentifierLength();
125
        } else {
126 144
            return 63;
127
        }
128
    }
129
130
    /**
131
     * Sets the Primary Key.
132
     *
133
     * @param array          $columns
134
     * @param string|boolean $indexName
135
     *
136
     * @return self
137
     */
138 334
    public function setPrimaryKey(array $columns, $indexName = false)
139
    {
140 334
        $this->_addIndex($this->_createIndex($columns, $indexName ?: "primary", true, true));
0 ignored issues
show
Bug introduced by
It seems like $indexName ?: 'primary' can also be of type true; however, parameter $indexName of Doctrine\DBAL\Schema\Table::_createIndex() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
        $this->_addIndex($this->_createIndex($columns, /** @scrutinizer ignore-type */ $indexName ?: "primary", true, true));
Loading history...
141
142 334
        foreach ($columns as $columnName) {
143 334
            $column = $this->getColumn($columnName);
144 334
            $column->setNotnull(true);
145
        }
146
147 334
        return $this;
148
    }
149
150
    /**
151
     * @param array       $columnNames
152
     * @param string|null $indexName
153
     * @param array       $flags
154
     * @param array       $options
155
     *
156
     * @return self
157
     */
158 101 View Code Duplication
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
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...
159
    {
160 101
        if ($indexName == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $indexName of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
161 27
            $indexName = $this->_generateIdentifierName(
162 27
                array_merge([$this->getName()], $columnNames), "idx", $this->_getMaxIdentifierLength()
163
            );
164
        }
165
166 101
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
167
    }
168
169
    /**
170
     * Drops the primary key from this table.
171
     *
172
     * @return void
173
     */
174 18
    public function dropPrimaryKey()
175
    {
176 18
        $this->dropIndex($this->_primaryKeyName);
177 18
        $this->_primaryKeyName = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_primaryKeyName was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
178 18
    }
179
180
    /**
181
     * Drops an index from this table.
182
     *
183
     * @param string $indexName The index name.
184
     *
185
     * @return void
186
     *
187
     * @throws SchemaException If the index does not exist.
188
     */
189 31 View Code Duplication
    public function dropIndex($indexName)
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...
190
    {
191 31
        $indexName = $this->normalizeIdentifier($indexName);
192 31
        if ( ! $this->hasIndex($indexName)) {
193
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
194
        }
195 31
        unset($this->_indexes[$indexName]);
196 31
    }
197
198
    /**
199
     * @param array       $columnNames
200
     * @param string|null $indexName
201
     * @param array       $options
202
     *
203
     * @return self
204
     */
205 31 View Code Duplication
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
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...
206
    {
207 31
        if ($indexName === null) {
208 23
            $indexName = $this->_generateIdentifierName(
209 23
                array_merge([$this->getName()], $columnNames), "uniq", $this->_getMaxIdentifierLength()
210
            );
211
        }
212
213 31
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
214
    }
215
216
    /**
217
     * Renames an index.
218
     *
219
     * @param string      $oldIndexName The name of the index to rename from.
220
     * @param string|null $newIndexName The name of the index to rename to.
221
     *                                  If null is given, the index name will be auto-generated.
222
     *
223
     * @return self This table instance.
224
     *
225
     * @throws SchemaException if no index exists for the given current name
226
     *                         or if an index with the given new name already exists on this table.
227
     */
228 13
    public function renameIndex($oldIndexName, $newIndexName = null)
229
    {
230 13
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
231 13
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
232
233 13
        if ($oldIndexName === $normalizedNewIndexName) {
234 9
            return $this;
235
        }
236
237 13
        if ( ! $this->hasIndex($oldIndexName)) {
238 1
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
239
        }
240
241 12
        if ($this->hasIndex($normalizedNewIndexName)) {
242 1
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
243
        }
244
245 11
        $oldIndex = $this->_indexes[$oldIndexName];
246
247 11
        if ($oldIndex->isPrimary()) {
248 1
            $this->dropPrimaryKey();
249
250 1
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName);
251
        }
252
253 11
        unset($this->_indexes[$oldIndexName]);
254
255 11
        if ($oldIndex->isUnique()) {
256 2
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
257
        }
258
259 10
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
260
    }
261
262
    /**
263
     * Checks if an index begins in the order of the given columns.
264
     *
265
     * @param array $columnsNames
266
     *
267
     * @return boolean
268
     */
269 1
    public function columnsAreIndexed(array $columnsNames)
270
    {
271 1
        foreach ($this->getIndexes() as $index) {
272
            /* @var $index Index */
273 1
            if ($index->spansColumns($columnsNames)) {
274 1
                return true;
275
            }
276
        }
277
278
        return false;
279
    }
280
281
    /**
282
     * @param array   $columnNames
283
     * @param string  $indexName
284
     * @param boolean $isUnique
285
     * @param boolean $isPrimary
286
     * @param array   $flags
287
     * @param array   $options
288
     *
289
     * @return Index
290
     *
291
     * @throws SchemaException
292
     */
293 477
    private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
294
    {
295 477
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
296 1
            throw SchemaException::indexNameInvalid($indexName);
297
        }
298
299 476
        foreach ($columnNames as $columnName => $indexColOptions) {
300 475
            if (is_numeric($columnName) && is_string($indexColOptions)) {
301 475
                $columnName = $indexColOptions;
302
            }
303
304 475
            if ( ! $this->hasColumn($columnName)) {
305 475
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
306
            }
307
        }
308
309 475
        return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
310
    }
311
312
    /**
313
     * @param string $columnName
314
     * @param string $typeName
315
     * @param array  $options
316
     *
317
     * @return Column
318
     */
319 659
    public function addColumn($columnName, $typeName, array $options=[])
320
    {
321 659
        $column = new Column($columnName, Type::getType($typeName), $options);
322
323 659
        $this->_addColumn($column);
324
325 659
        return $column;
326
    }
327
328
    /**
329
     * Renames a Column.
330
     *
331
     * @param string $oldColumnName
332
     * @param string $newColumnName
333
     *
334
     * @deprecated
335
     *
336
     * @throws DBALException
337
     */
338
    public function renameColumn($oldColumnName, $newColumnName)
0 ignored issues
show
Unused Code introduced by
The parameter $newColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

338
    public function renameColumn($oldColumnName, /** @scrutinizer ignore-unused */ $newColumnName)

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

Loading history...
Unused Code introduced by
The parameter $oldColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

338
    public function renameColumn(/** @scrutinizer ignore-unused */ $oldColumnName, $newColumnName)

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

Loading history...
339
    {
340
        throw new DBALException("Table#renameColumn() was removed, because it drops and recreates " .
341
            "the column instead. There is no fix available, because a schema diff cannot reliably detect if a " .
342
            "column was renamed or one column was created and another one dropped.");
343
    }
344
345
    /**
346
     * Change Column Details.
347
     *
348
     * @param string $columnName
349
     * @param array  $options
350
     *
351
     * @return self
352
     */
353 12
    public function changeColumn($columnName, array $options)
354
    {
355 12
        $column = $this->getColumn($columnName);
356 12
        $column->setOptions($options);
357
358 12
        return $this;
359
    }
360
361
    /**
362
     * Drops a Column from the Table.
363
     *
364
     * @param string $columnName
365
     *
366
     * @return self
367
     */
368 9
    public function dropColumn($columnName)
369
    {
370 9
        $columnName = $this->normalizeIdentifier($columnName);
371 9
        unset($this->_columns[$columnName]);
372
373 9
        return $this;
374
    }
375
376
    /**
377
     * Adds a foreign key constraint.
378
     *
379
     * Name is inferred from the local columns.
380
     *
381
     * @param Table|string $foreignTable Table schema instance or table name
382
     * @param array        $localColumnNames
383
     * @param array        $foreignColumnNames
384
     * @param array        $options
385
     * @param string|null  $constraintName
386
     *
387
     * @return self
388
     */
389 100
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=[], $constraintName = null)
390
    {
391 100
        $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength());
392
393 100
        return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
1 ignored issue
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() has been deprecated: Use {@link addForeignKeyConstraint} ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

393
        return /** @scrutinizer ignore-deprecated */ $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
394
    }
395
396
    /**
397
     * Adds a foreign key constraint.
398
     *
399
     * Name is to be generated by the database itself.
400
     *
401
     * @deprecated Use {@link addForeignKeyConstraint}
402
     *
403
     * @param Table|string $foreignTable Table schema instance or table name
404
     * @param array        $localColumnNames
405
     * @param array        $foreignColumnNames
406
     * @param array        $options
407
     *
408
     * @return self
409
     */
410 5
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=[])
411
    {
412 5
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
413
    }
414
415
    /**
416
     * Adds a foreign key constraint with a given name.
417
     *
418
     * @deprecated Use {@link addForeignKeyConstraint}
419
     *
420
     * @param string       $name
421
     * @param Table|string $foreignTable Table schema instance or table name
422
     * @param array        $localColumnNames
423
     * @param array        $foreignColumnNames
424
     * @param array        $options
425
     *
426
     * @return self
427
     *
428
     * @throws SchemaException
429
     */
430 101
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=[])
431
    {
432 101
        if ($foreignTable instanceof Table) {
433 52
            foreach ($foreignColumnNames as $columnName) {
434 52
                if ( ! $foreignTable->hasColumn($columnName)) {
435 52
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
436
                }
437
            }
438
        }
439
440 100
        foreach ($localColumnNames as $columnName) {
441 100
            if ( ! $this->hasColumn($columnName)) {
442 100
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
443
            }
444
        }
445
446 99
        $constraint = new ForeignKeyConstraint(
447 99
            $localColumnNames, $foreignTable, $foreignColumnNames, $name, $options
448
        );
449 99
        $this->_addForeignKeyConstraint($constraint);
450
451 99
        return $this;
452
    }
453
454
    /**
455
     * @param string $name
456
     * @param string $value
457
     *
458
     * @return self
459
     */
460 39
    public function addOption($name, $value)
461
    {
462 39
        $this->_options[$name] = $value;
463
464 39
        return $this;
465
    }
466
467
    /**
468
     * @param Column $column
469
     *
470
     * @return void
471
     *
472
     * @throws SchemaException
473
     */
474 689
    protected function _addColumn(Column $column)
475
    {
476 689
        $columnName = $column->getName();
477 689
        $columnName = $this->normalizeIdentifier($columnName);
478
479 689
        if (isset($this->_columns[$columnName])) {
480 1
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
481
        }
482
483 689
        $this->_columns[$columnName] = $column;
484 689
    }
485
486
    /**
487
     * Adds an index to the table.
488
     *
489
     * @param Index $indexCandidate
490
     *
491
     * @return self
492
     *
493
     * @throws SchemaException
494
     */
495 482
    protected function _addIndex(Index $indexCandidate)
496
    {
497 482
        $indexName = $indexCandidate->getName();
498 482
        $indexName = $this->normalizeIdentifier($indexName);
499 482
        $replacedImplicitIndexes = [];
500
501 482
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
502 31
            if ($implicitIndex->isFullfilledBy($indexCandidate) && isset($this->_indexes[$name])) {
503 31
                $replacedImplicitIndexes[] = $name;
504
            }
505
        }
506
507 482
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
508 482
            ($this->_primaryKeyName != false && $indexCandidate->isPrimary())
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->_primaryKeyName of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
509
        ) {
510 2
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
511
        }
512
513 482
        foreach ($replacedImplicitIndexes as $name) {
514 4
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
515
        }
516
517 482
        if ($indexCandidate->isPrimary()) {
518 336
            $this->_primaryKeyName = $indexName;
519
        }
520
521 482
        $this->_indexes[$indexName] = $indexCandidate;
522
523 482
        return $this;
524
    }
525
526
    /**
527
     * @param ForeignKeyConstraint $constraint
528
     *
529
     * @return void
530
     */
531 102
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
532
    {
533 102
        $constraint->setLocalTable($this);
534
535 102
        if (strlen($constraint->getName())) {
536 100
            $name = $constraint->getName();
537
        } else {
538 2
            $name = $this->_generateIdentifierName(
539 2
                array_merge((array) $this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength()
540
            );
541
        }
542 102
        $name = $this->normalizeIdentifier($name);
543
544 102
        $this->_fkConstraints[$name] = $constraint;
545
546
        // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request.
547
        // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
548
        // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns).
549 102
        $indexName = $this->_generateIdentifierName(
550 102
            array_merge([$this->getName()], $constraint->getColumns()),
551 102
            "idx",
552 102
            $this->_getMaxIdentifierLength()
553
        );
554 102
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
555
556 102
        foreach ($this->_indexes as $existingIndex) {
557 76
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
558 76
                return;
559
            }
560
        }
561
562 77
        $this->_addIndex($indexCandidate);
563 77
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
564 77
    }
565
566
    /**
567
     * Returns whether this table has a foreign key constraint with the given name.
568
     *
569
     * @param string $constraintName
570
     *
571
     * @return boolean
572
     */
573 10
    public function hasForeignKey($constraintName)
574
    {
575 10
        $constraintName = $this->normalizeIdentifier($constraintName);
576
577 10
        return isset($this->_fkConstraints[$constraintName]);
578
    }
579
580
    /**
581
     * Returns the foreign key constraint with the given name.
582
     *
583
     * @param string $constraintName The constraint name.
584
     *
585
     * @return ForeignKeyConstraint
586
     *
587
     * @throws SchemaException If the foreign key does not exist.
588
     */
589 8 View Code Duplication
    public function getForeignKey($constraintName)
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...
590
    {
591 8
        $constraintName = $this->normalizeIdentifier($constraintName);
592 8
        if (!$this->hasForeignKey($constraintName)) {
593
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
594
        }
595
596 8
        return $this->_fkConstraints[$constraintName];
597
    }
598
599
    /**
600
     * Removes the foreign key constraint with the given name.
601
     *
602
     * @param string $constraintName The constraint name.
603
     *
604
     * @return void
605
     *
606
     * @throws SchemaException
607
     */
608 10 View Code Duplication
    public function removeForeignKey($constraintName)
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...
609
    {
610 10
        $constraintName = $this->normalizeIdentifier($constraintName);
611 10
        if (!$this->hasForeignKey($constraintName)) {
612
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
613
        }
614
615 10
        unset($this->_fkConstraints[$constraintName]);
616 10
    }
617
618
    /**
619
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
620
     * @return Column[]
621
     */
622 489
    public function getColumns()
623
    {
624 489
        $primaryKeyColumns = [];
625 489
        if ($this->hasPrimaryKey()) {
626 222
            $primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns());
627
        }
628
629 489
        return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
630
    }
631
632
    /**
633
     * Returns foreign key columns
634
     * @return Column[]
635
     */
636 489
    private function getForeignKeyColumns()
637
    {
638 489
        $foreignKeyColumns = [];
639 489
        foreach ($this->getForeignKeys() as $foreignKey) {
640
            /* @var $foreignKey ForeignKeyConstraint */
641 45
            $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
642
        }
643 489
        return $this->filterColumns($foreignKeyColumns);
644
    }
645
646
    /**
647
     * Returns only columns that have specified names
648
     * @param array $columnNames
649
     * @return Column[]
650
     */
651
    private function filterColumns(array $columnNames)
652
    {
653 489
        return array_filter($this->_columns, function ($columnName) use ($columnNames) {
654 464
            return in_array($columnName, $columnNames, true);
655 489
        }, ARRAY_FILTER_USE_KEY);
656
    }
657
658
    /**
659
     * Returns whether this table has a Column with the given name.
660
     *
661
     * @param string $columnName The column name.
662
     *
663
     * @return boolean
664
     */
665 549
    public function hasColumn($columnName)
666
    {
667 549
        $columnName = $this->normalizeIdentifier($columnName);
668
669 549
        return isset($this->_columns[$columnName]);
670
    }
671
672
    /**
673
     * Returns the Column with the given name.
674
     *
675
     * @param string $columnName The column name.
676
     *
677
     * @return Column
678
     *
679
     * @throws SchemaException If the column does not exist.
680
     */
681 421
    public function getColumn($columnName)
682
    {
683 421
        $columnName = $this->normalizeIdentifier($columnName);
684 421
        if ( ! $this->hasColumn($columnName)) {
685 1
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
686
        }
687
688 420
        return $this->_columns[$columnName];
689
    }
690
691
    /**
692
     * Returns the primary key.
693
     *
694
     * @return Index|null The primary key, or null if this Table has no primary key.
695
     */
696 227
    public function getPrimaryKey()
697
    {
698 227
        if ( ! $this->hasPrimaryKey()) {
699
            return null;
700
        }
701
702 227
        return $this->getIndex($this->_primaryKeyName);
703
    }
704
705
    /**
706
     * Returns the primary key columns.
707
     *
708
     * @return array
709
     *
710
     * @throws DBALException
711
     */
712 13
    public function getPrimaryKeyColumns()
713
    {
714 13
        if ( ! $this->hasPrimaryKey()) {
715
            throw new DBALException("Table " . $this->getName() . " has no primary key.");
716
        }
717 13
        return $this->getPrimaryKey()->getColumns();
718
    }
719
720
    /**
721
     * Returns whether this table has a primary key.
722
     *
723
     * @return boolean
724
     */
725 497
    public function hasPrimaryKey()
726
    {
727 497
        return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName));
728
    }
729
730
    /**
731
     * Returns whether this table has an Index with the given name.
732
     *
733
     * @param string $indexName The index name.
734
     *
735
     * @return boolean
736
     */
737 279
    public function hasIndex($indexName)
738
    {
739 279
        $indexName = $this->normalizeIdentifier($indexName);
740
741 279
        return (isset($this->_indexes[$indexName]));
742
    }
743
744
    /**
745
     * Returns the Index with the given name.
746
     *
747
     * @param string $indexName The index name.
748
     *
749
     * @return Index
750
     *
751
     * @throws SchemaException If the index does not exist.
752
     */
753 257 View Code Duplication
    public function getIndex($indexName)
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...
754
    {
755 257
        $indexName = $this->normalizeIdentifier($indexName);
756 257
        if ( ! $this->hasIndex($indexName)) {
757 1
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
758
        }
759
760 256
        return $this->_indexes[$indexName];
761
    }
762
763
    /**
764
     * @return Index[]
765
     */
766 463
    public function getIndexes()
767
    {
768 463
        return $this->_indexes;
769
    }
770
771
    /**
772
     * Returns the foreign key constraints.
773
     *
774
     * @return ForeignKeyConstraint[]
775
     */
776 507
    public function getForeignKeys()
777
    {
778 507
        return $this->_fkConstraints;
779
    }
780
781
    /**
782
     * @param string $name
783
     *
784
     * @return boolean
785
     */
786 73
    public function hasOption($name)
787
    {
788 73
        return isset($this->_options[$name]);
789
    }
790
791
    /**
792
     * @param string $name
793
     *
794
     * @return mixed
795
     */
796 6
    public function getOption($name)
797
    {
798 6
        return $this->_options[$name];
799
    }
800
801
    /**
802
     * @return array
803
     */
804 365
    public function getOptions()
805
    {
806 365
        return $this->_options;
807
    }
808
809
    /**
810
     * @param Visitor $visitor
811
     *
812
     * @return void
813
     */
814 18
    public function visit(Visitor $visitor)
815
    {
816 18
        $visitor->acceptTable($this);
817
818 18
        foreach ($this->getColumns() as $column) {
819 15
            $visitor->acceptColumn($this, $column);
820
        }
821
822 18
        foreach ($this->getIndexes() as $index) {
823 11
            $visitor->acceptIndex($this, $index);
824
        }
825
826 18
        foreach ($this->getForeignKeys() as $constraint) {
827 4
            $visitor->acceptForeignKey($this, $constraint);
828
        }
829 18
    }
830
831
    /**
832
     * Clone of a Table triggers a deep clone of all affected assets.
833
     *
834
     * @return void
835
     */
836 57
    public function __clone()
837
    {
838 57
        foreach ($this->_columns as $k => $column) {
839 56
            $this->_columns[$k] = clone $column;
840
        }
841 57
        foreach ($this->_indexes as $k => $index) {
842 35
            $this->_indexes[$k] = clone $index;
843
        }
844 57
        foreach ($this->_fkConstraints as $k => $fk) {
845 7
            $this->_fkConstraints[$k] = clone $fk;
846 7
            $this->_fkConstraints[$k]->setLocalTable($this);
847
        }
848 57
    }
849
850
    /**
851
     * Normalizes a given identifier.
852
     *
853
     * Trims quotes and lowercases the given identifier.
854
     *
855
     * @param string $identifier The identifier to normalize.
856
     *
857
     * @return string The normalized identifier.
858
     */
859 693
    private function normalizeIdentifier($identifier)
860
    {
861 693
        return $this->trimQuotes(strtolower($identifier));
862
    }
863
}
864