Failed Conditions
Pull Request — master (#3512)
by David
16:57
created

Table::getComment()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Visitor\Visitor;
7
use Doctrine\DBAL\Types\Type;
8
use const ARRAY_FILTER_USE_KEY;
9
use function array_filter;
10
use function array_merge;
11
use function in_array;
12
use function preg_match;
13
use function strlen;
14
use function strtolower;
15
16
/**
17
 * Object Representation of a table.
18
 */
19
class Table extends AbstractAsset
20
{
21
    /** @var Column[] */
22
    protected $_columns = [];
23
24
    /** @var Index[] */
25
    private $implicitIndexes = [];
26
27
    /** @var Index[] */
28
    protected $_indexes = [];
29
30
    /** @var string */
31
    protected $_primaryKeyName = false;
32
33
    /** @var ForeignKeyConstraint[] */
34
    protected $_fkConstraints = [];
35
36
    /** @var mixed[] */
37
    protected $_options = [
38
        'create_options' => [],
39
    ];
40
41
    /** @var SchemaConfig|null */
42
    protected $_schemaConfig = null;
43
44
    /**
45
     * @param string                 $tableName
46
     * @param Column[]               $columns
47
     * @param Index[]                $indexes
48
     * @param ForeignKeyConstraint[] $fkConstraints
49
     * @param int                    $idGeneratorType
50
     * @param mixed[]                $options
51
     *
52
     * @throws DBALException
53
     */
54 24757
    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

54
    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...
55
    {
56 24757
        if (strlen($tableName) === 0) {
57 1561
            throw DBALException::invalidTableName($tableName);
58
        }
59
60 24755
        $this->_setName($tableName);
61
62 24755
        foreach ($columns as $column) {
63 22233
            $this->_addColumn($column);
64
        }
65
66 24753
        foreach ($indexes as $idx) {
67 21993
            $this->_addIndex($idx);
68
        }
69
70 24749
        foreach ($fkConstraints as $constraint) {
71 21036
            $this->_addForeignKeyConstraint($constraint);
72
        }
73
74 24749
        $this->_options = array_merge($this->_options, $options);
75 24749
    }
76
77
    /**
78
     * @return void
79
     */
80 22711
    public function setSchemaConfig(SchemaConfig $schemaConfig)
81
    {
82 22711
        $this->_schemaConfig = $schemaConfig;
83 22711
    }
84
85
    /**
86
     * @return int
87
     */
88 22972
    protected function _getMaxIdentifierLength()
89
    {
90 22972
        if ($this->_schemaConfig instanceof SchemaConfig) {
91 22507
            return $this->_schemaConfig->getMaxIdentifierLength();
92
        }
93
94 22906
        return 63;
95
    }
96
97
    /**
98
     * Sets the Primary Key.
99
     *
100
     * @param string[]     $columnNames
101
     * @param string|false $indexName
102
     *
103
     * @return self
104
     */
105 23675
    public function setPrimaryKey(array $columnNames, $indexName = false)
106
    {
107 23675
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
108
109 23675
        foreach ($columnNames as $columnName) {
110 23675
            $column = $this->getColumn($columnName);
111 23675
            $column->setNotnull(true);
112
        }
113
114 23675
        return $this;
115
    }
116
117
    /**
118
     * @param string[]    $columnNames
119
     * @param string|null $indexName
120
     * @param string[]    $flags
121
     * @param mixed[]     $options
122
     *
123
     * @return self
124
     */
125 21494
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
126
    {
127 21494
        if ($indexName === null) {
128 20971
            $indexName = $this->_generateIdentifierName(
129 20971
                array_merge([$this->getName()], $columnNames),
130 20971
                'idx',
131 20971
                $this->_getMaxIdentifierLength()
132
            );
133
        }
134
135 21494
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
136
    }
137
138
    /**
139
     * Drops the primary key from this table.
140
     *
141
     * @return void
142
     */
143 20260
    public function dropPrimaryKey()
144
    {
145 20260
        $this->dropIndex($this->_primaryKeyName);
146 20260
        $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...
147 20260
    }
148
149
    /**
150
     * Drops an index from this table.
151
     *
152
     * @param string $indexName The index name.
153
     *
154
     * @return void
155
     *
156
     * @throws SchemaException If the index does not exist.
157
     */
158 20297
    public function dropIndex($indexName)
159
    {
160 20297
        $indexName = $this->normalizeIdentifier($indexName);
161 20297
        if (! $this->hasIndex($indexName)) {
162
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
163
        }
164 20297
        unset($this->_indexes[$indexName]);
165 20297
    }
166
167
    /**
168
     * @param string[]    $columnNames
169
     * @param string|null $indexName
170
     * @param mixed[]     $options
171
     *
172
     * @return self
173
     */
174 22565
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
175
    {
176 22565
        if ($indexName === null) {
177 22208
            $indexName = $this->_generateIdentifierName(
178 22208
                array_merge([$this->getName()], $columnNames),
179 22208
                'uniq',
180 22208
                $this->_getMaxIdentifierLength()
181
            );
182
        }
183
184 22565
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
185
    }
186
187
    /**
188
     * Renames an index.
189
     *
190
     * @param string      $oldIndexName The name of the index to rename from.
191
     * @param string|null $newIndexName The name of the index to rename to.
192
     *                                  If null is given, the index name will be auto-generated.
193
     *
194
     * @return self This table instance.
195
     *
196
     * @throws SchemaException If no index exists for the given current name
197
     *                         or if an index with the given new name already exists on this table.
198
     */
199 19859
    public function renameIndex($oldIndexName, $newIndexName = null)
200
    {
201 19859
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
202 19859
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
203
204 19859
        if ($oldIndexName === $normalizedNewIndexName) {
205 427
            return $this;
206
        }
207
208 19859
        if (! $this->hasIndex($oldIndexName)) {
209 336
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
210
        }
211
212 19857
        if ($this->hasIndex($normalizedNewIndexName)) {
213 311
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
214
        }
215
216 19855
        $oldIndex = $this->_indexes[$oldIndexName];
217
218 19855
        if ($oldIndex->isPrimary()) {
219 411
            $this->dropPrimaryKey();
220
221 411
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
222
        }
223
224 19855
        unset($this->_indexes[$oldIndexName]);
225
226 19855
        if ($oldIndex->isUnique()) {
227 413
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
228
        }
229
230 19853
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
231
    }
232
233
    /**
234
     * Checks if an index begins in the order of the given columns.
235
     *
236
     * @param string[] $columnNames
237
     *
238
     * @return bool
239
     */
240 20034
    public function columnsAreIndexed(array $columnNames)
241
    {
242 20034
        foreach ($this->getIndexes() as $index) {
243
            /** @var $index Index */
244 20034
            if ($index->spansColumns($columnNames)) {
245 20034
                return true;
246
            }
247
        }
248
249
        return false;
250
    }
251
252
    /**
253
     * @param string[] $columnNames
254
     * @param string   $indexName
255
     * @param bool     $isUnique
256
     * @param bool     $isPrimary
257
     * @param string[] $flags
258
     * @param mixed[]  $options
259
     *
260
     * @return Index
261
     *
262
     * @throws SchemaException
263
     */
264 23973
    private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
265
    {
266 23973
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
267 1086
            throw SchemaException::indexNameInvalid($indexName);
268
        }
269
270 23971
        foreach ($columnNames as $columnName) {
271 23969
            if (! $this->hasColumn($columnName)) {
272 2055
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
273
            }
274
        }
275
276 23969
        return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
277
    }
278
279
    /**
280
     * @param string  $columnName
281
     * @param string  $typeName
282
     * @param mixed[] $options
283
     *
284
     * @return Column
285
     */
286 24403
    public function addColumn($columnName, $typeName, array $options = [])
287
    {
288 24403
        $column = new Column($columnName, Type::getType($typeName), $options);
289
290 24403
        $this->_addColumn($column);
291
292 24403
        return $column;
293
    }
294
295
    /**
296
     * Renames a Column.
297
     *
298
     * @deprecated
299
     *
300
     * @param string $oldColumnName
301
     * @param string $newColumnName
302
     *
303
     * @throws DBALException
304
     */
305
    public function renameColumn($oldColumnName, $newColumnName)
0 ignored issues
show
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

305
    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...
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

305
    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...
306
    {
307
        throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' .
308
            'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
309
            'column was renamed or one column was created and another one dropped.');
310
    }
311
312
    /**
313
     * Change Column Details.
314
     *
315
     * @param string  $columnName
316
     * @param mixed[] $options
317
     *
318
     * @return self
319
     */
320 20482
    public function changeColumn($columnName, array $options)
321
    {
322 20482
        $column = $this->getColumn($columnName);
323 20482
        $column->setOptions($options);
324
325 20482
        return $this;
326
    }
327
328
    /**
329
     * Drops a Column from the Table.
330
     *
331
     * @param string $columnName
332
     *
333
     * @return self
334
     */
335 1452
    public function dropColumn($columnName)
336
    {
337 1452
        $columnName = $this->normalizeIdentifier($columnName);
338 1452
        unset($this->_columns[$columnName]);
339
340 1452
        return $this;
341
    }
342
343
    /**
344
     * Adds a foreign key constraint.
345
     *
346
     * Name is inferred from the local columns.
347
     *
348
     * @param Table|string $foreignTable       Table schema instance or table name
349
     * @param string[]     $localColumnNames
350
     * @param string[]     $foreignColumnNames
351
     * @param mixed[]      $options
352
     * @param string|null  $constraintName
353
     *
354
     * @return self
355
     */
356 22734
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
357
    {
358 22734
        $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
359
360 22734
        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

360
        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...
361
    }
362
363
    /**
364
     * Adds a foreign key constraint.
365
     *
366
     * Name is to be generated by the database itself.
367
     *
368
     * @deprecated Use {@link addForeignKeyConstraint}
369
     *
370
     * @param Table|string $foreignTable       Table schema instance or table name
371
     * @param string[]     $localColumnNames
372
     * @param string[]     $foreignColumnNames
373
     * @param mixed[]      $options
374
     *
375
     * @return self
376
     */
377 15983
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
378
    {
379 15983
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
380
    }
381
382
    /**
383
     * Adds a foreign key constraint with a given name.
384
     *
385
     * @deprecated Use {@link addForeignKeyConstraint}
386
     *
387
     * @param string       $name
388
     * @param Table|string $foreignTable       Table schema instance or table name
389
     * @param string[]     $localColumnNames
390
     * @param string[]     $foreignColumnNames
391
     * @param mixed[]      $options
392
     *
393
     * @return self
394
     *
395
     * @throws SchemaException
396
     */
397 22736
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
398
    {
399 22736
        if ($foreignTable instanceof Table) {
400 22246
            foreach ($foreignColumnNames as $columnName) {
401 22246
                if (! $foreignTable->hasColumn($columnName)) {
402 1100
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
403
                }
404
            }
405
        }
406
407 22734
        foreach ($localColumnNames as $columnName) {
408 22734
            if (! $this->hasColumn($columnName)) {
409 1235
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
410
            }
411
        }
412
413 22732
        $constraint = new ForeignKeyConstraint(
414 22732
            $localColumnNames,
415 224
            $foreignTable,
416 224
            $foreignColumnNames,
417 224
            $name,
418 224
            $options
419
        );
420 22732
        $this->_addForeignKeyConstraint($constraint);
421
422 22732
        return $this;
423
    }
424
425
    /**
426
     * @param string $name
427
     * @param mixed  $value
428
     *
429
     * @return self
430
     */
431 22169
    public function addOption($name, $value)
432
    {
433 22169
        $this->_options[$name] = $value;
434
435 22169
        return $this;
436
    }
437
438
    /**
439
     * @return void
440
     *
441
     * @throws SchemaException
442
     */
443 24507
    protected function _addColumn(Column $column)
444
    {
445 24507
        $columnName = $column->getName();
446 24507
        $columnName = $this->normalizeIdentifier($columnName);
447
448 24507
        if (isset($this->_columns[$columnName])) {
449 1386
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
450
        }
451
452 24507
        $this->_columns[$columnName] = $column;
453 24507
    }
454
455
    /**
456
     * Adds an index to the table.
457
     *
458
     * @return self
459
     *
460
     * @throws SchemaException
461
     */
462 23983
    protected function _addIndex(Index $indexCandidate)
463
    {
464 23983
        $indexName               = $indexCandidate->getName();
465 23983
        $indexName               = $this->normalizeIdentifier($indexName);
466 23983
        $replacedImplicitIndexes = [];
467
468 23983
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
469 19005
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
470 18997
                continue;
471
            }
472
473 692
            $replacedImplicitIndexes[] = $name;
474
        }
475
476 23983
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
477 23983
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
478
        ) {
479 1263
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
480
        }
481
482 23983
        foreach ($replacedImplicitIndexes as $name) {
483 692
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
484
        }
485
486 23983
        if ($indexCandidate->isPrimary()) {
487 23679
            $this->_primaryKeyName = $indexName;
488
        }
489
490 23983
        $this->_indexes[$indexName] = $indexCandidate;
491
492 23983
        return $this;
493
    }
494
495
    /**
496
     * @return void
497
     */
498 22818
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
499
    {
500 22818
        $constraint->setLocalTable($this);
501
502 22818
        if (strlen($constraint->getName())) {
503 22740
            $name = $constraint->getName();
504
        } else {
505 1981
            $name = $this->_generateIdentifierName(
506 1981
                array_merge((array) $this->getName(), $constraint->getLocalColumns()),
507 1981
                'fk',
508 1981
                $this->_getMaxIdentifierLength()
509
            );
510
        }
511 22818
        $name = $this->normalizeIdentifier($name);
512
513 22818
        $this->_fkConstraints[$name] = $constraint;
514
515
        // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request.
516
        // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
517
        // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns).
518 22818
        $indexName      = $this->_generateIdentifierName(
519 22818
            array_merge([$this->getName()], $constraint->getColumns()),
520 22818
            'idx',
521 22818
            $this->_getMaxIdentifierLength()
522
        );
523 22818
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
524
525 22818
        foreach ($this->_indexes as $existingIndex) {
526 22766
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
527 21118
                return;
528
            }
529
        }
530
531 22758
        $this->_addIndex($indexCandidate);
532 22758
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
533 22758
    }
534
535
    /**
536
     * Returns whether this table has a foreign key constraint with the given name.
537
     *
538
     * @param string $constraintName
539
     *
540
     * @return bool
541
     */
542 19850
    public function hasForeignKey($constraintName)
543
    {
544 19850
        $constraintName = $this->normalizeIdentifier($constraintName);
545
546 19850
        return isset($this->_fkConstraints[$constraintName]);
547
    }
548
549
    /**
550
     * Returns the foreign key constraint with the given name.
551
     *
552
     * @param string $constraintName The constraint name.
553
     *
554
     * @return ForeignKeyConstraint
555
     *
556
     * @throws SchemaException If the foreign key does not exist.
557
     */
558 302
    public function getForeignKey($constraintName)
559
    {
560 302
        $constraintName = $this->normalizeIdentifier($constraintName);
561 302
        if (! $this->hasForeignKey($constraintName)) {
562
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
563
        }
564
565 302
        return $this->_fkConstraints[$constraintName];
566
    }
567
568
    /**
569
     * Removes the foreign key constraint with the given name.
570
     *
571
     * @param string $constraintName The constraint name.
572
     *
573
     * @return void
574
     *
575
     * @throws SchemaException
576
     */
577 304
    public function removeForeignKey($constraintName)
578
    {
579 304
        $constraintName = $this->normalizeIdentifier($constraintName);
580 304
        if (! $this->hasForeignKey($constraintName)) {
581
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
582
        }
583
584 304
        unset($this->_fkConstraints[$constraintName]);
585 304
    }
586
587
    /**
588
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
589
     *
590
     * @return Column[]
591
     */
592 24057
    public function getColumns()
593
    {
594 24057
        $primaryKey        = $this->getPrimaryKey();
595 24057
        $primaryKeyColumns = [];
596
597 24057
        if ($primaryKey !== null) {
598 23439
            $primaryKeyColumns = $this->filterColumns($primaryKey->getColumns());
599
        }
600
601 24057
        return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
602
    }
603
604
    /**
605
     * Returns foreign key columns
606
     *
607
     * @return Column[]
608
     */
609 24057
    private function getForeignKeyColumns()
610
    {
611 24057
        $foreignKeyColumns = [];
612 24057
        foreach ($this->getForeignKeys() as $foreignKey) {
613 22694
            $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
614
        }
615
616 24057
        return $this->filterColumns($foreignKeyColumns);
617
    }
618
619
    /**
620
     * Returns only columns that have specified names
621
     *
622
     * @param string[] $columnNames
623
     *
624
     * @return Column[]
625
     */
626 24057
    private function filterColumns(array $columnNames)
627
    {
628
        return array_filter($this->_columns, static function ($columnName) use ($columnNames) {
629 24005
            return in_array($columnName, $columnNames, true);
630 24057
        }, ARRAY_FILTER_USE_KEY);
631
    }
632
633
    /**
634
     * Returns whether this table has a Column with the given name.
635
     *
636
     * @param string $columnName The column name.
637
     *
638
     * @return bool
639
     */
640 24171
    public function hasColumn($columnName)
641
    {
642 24171
        $columnName = $this->normalizeIdentifier($columnName);
643
644 24171
        return isset($this->_columns[$columnName]);
645
    }
646
647
    /**
648
     * Returns the Column with the given name.
649
     *
650
     * @param string $columnName The column name.
651
     *
652
     * @return Column
653
     *
654
     * @throws SchemaException If the column does not exist.
655
     */
656 23903
    public function getColumn($columnName)
657
    {
658 23903
        $columnName = $this->normalizeIdentifier($columnName);
659 23903
        if (! $this->hasColumn($columnName)) {
660 1411
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
661
        }
662
663 23901
        return $this->_columns[$columnName];
664
    }
665
666
    /**
667
     * Returns the primary key.
668
     *
669
     * @return Index|null The primary key, or null if this Table has no primary key.
670
     */
671 24067
    public function getPrimaryKey()
672
    {
673 24067
        if (! $this->hasPrimaryKey()) {
674 23291
            return null;
675
        }
676
677 23449
        return $this->getIndex($this->_primaryKeyName);
678
    }
679
680
    /**
681
     * Returns the primary key columns.
682
     *
683
     * @return string[]
684
     *
685
     * @throws DBALException
686
     */
687 20250
    public function getPrimaryKeyColumns()
688
    {
689 20250
        $primaryKey = $this->getPrimaryKey();
690
691 20250
        if ($primaryKey === null) {
692
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
693
        }
694
695 20250
        return $primaryKey->getColumns();
696
    }
697
698
    /**
699
     * Returns whether this table has a primary key.
700
     *
701
     * @return bool
702
     */
703 24073
    public function hasPrimaryKey()
704
    {
705 24073
        return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
706
    }
707
708
    /**
709
     * Returns whether this table has an Index with the given name.
710
     *
711
     * @param string $indexName The index name.
712
     *
713
     * @return bool
714
     */
715 23553
    public function hasIndex($indexName)
716
    {
717 23553
        $indexName = $this->normalizeIdentifier($indexName);
718
719 23553
        return isset($this->_indexes[$indexName]);
720
    }
721
722
    /**
723
     * Returns the Index with the given name.
724
     *
725
     * @param string $indexName The index name.
726
     *
727
     * @return Index
728
     *
729
     * @throws SchemaException If the index does not exist.
730
     */
731 23509
    public function getIndex($indexName)
732
    {
733 23509
        $indexName = $this->normalizeIdentifier($indexName);
734 23509
        if (! $this->hasIndex($indexName)) {
735 1286
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
736
        }
737
738 23507
        return $this->_indexes[$indexName];
739
    }
740
741
    /**
742
     * @return Index[]
743
     */
744 24001
    public function getIndexes()
745
    {
746 24001
        return $this->_indexes;
747
    }
748
749
    /**
750
     * Returns the foreign key constraints.
751
     *
752
     * @return ForeignKeyConstraint[]
753
     */
754 24093
    public function getForeignKeys()
755
    {
756 24093
        return $this->_fkConstraints;
757
    }
758
759
    /**
760
     * @param string $name
761
     *
762
     * @return bool
763
     */
764 22782
    public function hasOption($name)
765
    {
766 22782
        return isset($this->_options[$name]);
767
    }
768
769
    /**
770
     * @param string $name
771
     *
772
     * @return mixed
773
     */
774 20590
    public function getOption($name)
775
    {
776 20590
        return $this->_options[$name];
777
    }
778
779
    /**
780
     * @return mixed[]
781
     */
782 23787
    public function getOptions()
783
    {
784 23787
        return $this->_options;
785
    }
786
787
    /**
788
     * @return void
789
     */
790 22428
    public function visit(Visitor $visitor)
791
    {
792 22428
        $visitor->acceptTable($this);
793
794 22428
        foreach ($this->getColumns() as $column) {
795 22422
            $visitor->acceptColumn($this, $column);
796
        }
797
798 22428
        foreach ($this->getIndexes() as $index) {
799 20265
            $visitor->acceptIndex($this, $index);
800
        }
801
802 22428
        foreach ($this->getForeignKeys() as $constraint) {
803 158
            $visitor->acceptForeignKey($this, $constraint);
804
        }
805 22428
    }
806
807
    /**
808
     * Clone of a Table triggers a deep clone of all affected assets.
809
     *
810
     * @return void
811
     */
812 21705
    public function __clone()
813
    {
814 21705
        foreach ($this->_columns as $k => $column) {
815 21703
            $this->_columns[$k] = clone $column;
816
        }
817 21705
        foreach ($this->_indexes as $k => $index) {
818 21681
            $this->_indexes[$k] = clone $index;
819
        }
820 21705
        foreach ($this->_fkConstraints as $k => $fk) {
821 20525
            $this->_fkConstraints[$k] = clone $fk;
822 20525
            $this->_fkConstraints[$k]->setLocalTable($this);
823
        }
824 21705
    }
825
826
    /**
827
     * Normalizes a given identifier.
828
     *
829
     * Trims quotes and lowercases the given identifier.
830
     *
831
     * @param string|null $identifier The identifier to normalize.
832
     *
833
     * @return string The normalized identifier.
834
     */
835 24515
    private function normalizeIdentifier($identifier)
836
    {
837 24515
        if ($identifier === null) {
838 411
            return '';
839
        }
840
841 24515
        return $this->trimQuotes(strtolower($identifier));
842
    }
843
844 20026
    public function setComment(?string $comment) : self
845
    {
846
        // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options.
847 20026
        $this->addOption('comment', $comment);
848
849 20026
        return $this;
850
    }
851
852 20026
    public function getComment() : ?string
853
    {
854 20026
        return $this->_options['comment'] ?? null;
855
    }
856
}
857