Table.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. <?php
  2. namespace Doctrine\DBAL\Schema;
  3. use Doctrine\DBAL\Exception;
  4. use Doctrine\DBAL\Schema\Visitor\Visitor;
  5. use Doctrine\DBAL\Types\Type;
  6. use function array_filter;
  7. use function array_merge;
  8. use function in_array;
  9. use function preg_match;
  10. use function strlen;
  11. use function strtolower;
  12. use const ARRAY_FILTER_USE_KEY;
  13. /**
  14. * Object Representation of a table.
  15. */
  16. class Table extends AbstractAsset
  17. {
  18. /** @var Column[] */
  19. protected $_columns = [];
  20. /** @var Index[] */
  21. private $implicitIndexes = [];
  22. /** @var Index[] */
  23. protected $_indexes = [];
  24. /** @var string|false */
  25. protected $_primaryKeyName = false;
  26. /** @var ForeignKeyConstraint[] */
  27. protected $_fkConstraints = [];
  28. /** @var mixed[] */
  29. protected $_options = [
  30. 'create_options' => [],
  31. ];
  32. /** @var SchemaConfig|null */
  33. protected $_schemaConfig;
  34. /**
  35. * @param string $name
  36. * @param Column[] $columns
  37. * @param Index[] $indexes
  38. * @param ForeignKeyConstraint[] $fkConstraints
  39. * @param int $idGeneratorType
  40. * @param mixed[] $options
  41. *
  42. * @throws Exception
  43. */
  44. public function __construct(
  45. $name,
  46. array $columns = [],
  47. array $indexes = [],
  48. array $fkConstraints = [],
  49. $idGeneratorType = 0,
  50. array $options = []
  51. ) {
  52. if (strlen($name) === 0) {
  53. throw Exception::invalidTableName($name);
  54. }
  55. $this->_setName($name);
  56. foreach ($columns as $column) {
  57. $this->_addColumn($column);
  58. }
  59. foreach ($indexes as $idx) {
  60. $this->_addIndex($idx);
  61. }
  62. foreach ($fkConstraints as $constraint) {
  63. $this->_addForeignKeyConstraint($constraint);
  64. }
  65. $this->_options = array_merge($this->_options, $options);
  66. }
  67. /**
  68. * @return void
  69. */
  70. public function setSchemaConfig(SchemaConfig $schemaConfig)
  71. {
  72. $this->_schemaConfig = $schemaConfig;
  73. }
  74. /**
  75. * @return int
  76. */
  77. protected function _getMaxIdentifierLength()
  78. {
  79. if ($this->_schemaConfig instanceof SchemaConfig) {
  80. return $this->_schemaConfig->getMaxIdentifierLength();
  81. }
  82. return 63;
  83. }
  84. /**
  85. * Sets the Primary Key.
  86. *
  87. * @param string[] $columnNames
  88. * @param string|false $indexName
  89. *
  90. * @return self
  91. */
  92. public function setPrimaryKey(array $columnNames, $indexName = false)
  93. {
  94. $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
  95. foreach ($columnNames as $columnName) {
  96. $column = $this->getColumn($columnName);
  97. $column->setNotnull(true);
  98. }
  99. return $this;
  100. }
  101. /**
  102. * @param string[] $columnNames
  103. * @param string|null $indexName
  104. * @param string[] $flags
  105. * @param mixed[] $options
  106. *
  107. * @return self
  108. */
  109. public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
  110. {
  111. if ($indexName === null) {
  112. $indexName = $this->_generateIdentifierName(
  113. array_merge([$this->getName()], $columnNames),
  114. 'idx',
  115. $this->_getMaxIdentifierLength()
  116. );
  117. }
  118. return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
  119. }
  120. /**
  121. * Drops the primary key from this table.
  122. *
  123. * @return void
  124. */
  125. public function dropPrimaryKey()
  126. {
  127. if ($this->_primaryKeyName === false) {
  128. return;
  129. }
  130. $this->dropIndex($this->_primaryKeyName);
  131. $this->_primaryKeyName = false;
  132. }
  133. /**
  134. * Drops an index from this table.
  135. *
  136. * @param string $name The index name.
  137. *
  138. * @return void
  139. *
  140. * @throws SchemaException If the index does not exist.
  141. */
  142. public function dropIndex($name)
  143. {
  144. $name = $this->normalizeIdentifier($name);
  145. if (! $this->hasIndex($name)) {
  146. throw SchemaException::indexDoesNotExist($name, $this->_name);
  147. }
  148. unset($this->_indexes[$name]);
  149. }
  150. /**
  151. * @param string[] $columnNames
  152. * @param string|null $indexName
  153. * @param mixed[] $options
  154. *
  155. * @return self
  156. */
  157. public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
  158. {
  159. if ($indexName === null) {
  160. $indexName = $this->_generateIdentifierName(
  161. array_merge([$this->getName()], $columnNames),
  162. 'uniq',
  163. $this->_getMaxIdentifierLength()
  164. );
  165. }
  166. return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
  167. }
  168. /**
  169. * Renames an index.
  170. *
  171. * @param string $oldName The name of the index to rename from.
  172. * @param string|null $newName The name of the index to rename to.
  173. * If null is given, the index name will be auto-generated.
  174. *
  175. * @return self This table instance.
  176. *
  177. * @throws SchemaException If no index exists for the given current name
  178. * or if an index with the given new name already exists on this table.
  179. */
  180. public function renameIndex($oldName, $newName = null)
  181. {
  182. $oldName = $this->normalizeIdentifier($oldName);
  183. $normalizedNewName = $this->normalizeIdentifier($newName);
  184. if ($oldName === $normalizedNewName) {
  185. return $this;
  186. }
  187. if (! $this->hasIndex($oldName)) {
  188. throw SchemaException::indexDoesNotExist($oldName, $this->_name);
  189. }
  190. if ($this->hasIndex($normalizedNewName)) {
  191. throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name);
  192. }
  193. $oldIndex = $this->_indexes[$oldName];
  194. if ($oldIndex->isPrimary()) {
  195. $this->dropPrimaryKey();
  196. return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false);
  197. }
  198. unset($this->_indexes[$oldName]);
  199. if ($oldIndex->isUnique()) {
  200. return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions());
  201. }
  202. return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions());
  203. }
  204. /**
  205. * Checks if an index begins in the order of the given columns.
  206. *
  207. * @param string[] $columnNames
  208. *
  209. * @return bool
  210. */
  211. public function columnsAreIndexed(array $columnNames)
  212. {
  213. foreach ($this->getIndexes() as $index) {
  214. if ($index->spansColumns($columnNames)) {
  215. return true;
  216. }
  217. }
  218. return false;
  219. }
  220. /**
  221. * @param string[] $columnNames
  222. * @param string $indexName
  223. * @param bool $isUnique
  224. * @param bool $isPrimary
  225. * @param string[] $flags
  226. * @param mixed[] $options
  227. *
  228. * @return Index
  229. *
  230. * @throws SchemaException
  231. */
  232. private function _createIndex(
  233. array $columnNames,
  234. $indexName,
  235. $isUnique,
  236. $isPrimary,
  237. array $flags = [],
  238. array $options = []
  239. ) {
  240. if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
  241. throw SchemaException::indexNameInvalid($indexName);
  242. }
  243. foreach ($columnNames as $columnName) {
  244. if (! $this->hasColumn($columnName)) {
  245. throw SchemaException::columnDoesNotExist($columnName, $this->_name);
  246. }
  247. }
  248. return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
  249. }
  250. /**
  251. * @param string $name
  252. * @param string $typeName
  253. * @param mixed[] $options
  254. *
  255. * @return Column
  256. */
  257. public function addColumn($name, $typeName, array $options = [])
  258. {
  259. $column = new Column($name, Type::getType($typeName), $options);
  260. $this->_addColumn($column);
  261. return $column;
  262. }
  263. /**
  264. * Renames a Column.
  265. *
  266. * @deprecated
  267. *
  268. * @param string $oldName
  269. * @param string $name
  270. *
  271. * @return void
  272. *
  273. * @throws Exception
  274. */
  275. public function renameColumn($oldName, $name)
  276. {
  277. throw new Exception('Table#renameColumn() was removed, because it drops and recreates ' .
  278. 'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
  279. 'column was renamed or one column was created and another one dropped.');
  280. }
  281. /**
  282. * Change Column Details.
  283. *
  284. * @param string $name
  285. * @param mixed[] $options
  286. *
  287. * @return self
  288. */
  289. public function changeColumn($name, array $options)
  290. {
  291. $column = $this->getColumn($name);
  292. $column->setOptions($options);
  293. return $this;
  294. }
  295. /**
  296. * Drops a Column from the Table.
  297. *
  298. * @param string $name
  299. *
  300. * @return self
  301. */
  302. public function dropColumn($name)
  303. {
  304. $name = $this->normalizeIdentifier($name);
  305. unset($this->_columns[$name]);
  306. return $this;
  307. }
  308. /**
  309. * Adds a foreign key constraint.
  310. *
  311. * Name is inferred from the local columns.
  312. *
  313. * @param Table|string $foreignTable Table schema instance or table name
  314. * @param string[] $localColumnNames
  315. * @param string[] $foreignColumnNames
  316. * @param mixed[] $options
  317. * @param string|null $constraintName
  318. *
  319. * @return self
  320. */
  321. public function addForeignKeyConstraint(
  322. $foreignTable,
  323. array $localColumnNames,
  324. array $foreignColumnNames,
  325. array $options = [],
  326. $constraintName = null
  327. ) {
  328. $constraintName = $constraintName ?: $this->_generateIdentifierName(
  329. array_merge((array) $this->getName(), $localColumnNames),
  330. 'fk',
  331. $this->_getMaxIdentifierLength()
  332. );
  333. return $this->addNamedForeignKeyConstraint(
  334. $constraintName,
  335. $foreignTable,
  336. $localColumnNames,
  337. $foreignColumnNames,
  338. $options
  339. );
  340. }
  341. /**
  342. * Adds a foreign key constraint.
  343. *
  344. * Name is to be generated by the database itself.
  345. *
  346. * @deprecated Use {@link addForeignKeyConstraint}
  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. *
  353. * @return self
  354. */
  355. public function addUnnamedForeignKeyConstraint(
  356. $foreignTable,
  357. array $localColumnNames,
  358. array $foreignColumnNames,
  359. array $options = []
  360. ) {
  361. return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
  362. }
  363. /**
  364. * Adds a foreign key constraint with a given name.
  365. *
  366. * @deprecated Use {@link addForeignKeyConstraint}
  367. *
  368. * @param string $name
  369. * @param Table|string $foreignTable Table schema instance or table name
  370. * @param string[] $localColumnNames
  371. * @param string[] $foreignColumnNames
  372. * @param mixed[] $options
  373. *
  374. * @return self
  375. *
  376. * @throws SchemaException
  377. */
  378. public function addNamedForeignKeyConstraint(
  379. $name,
  380. $foreignTable,
  381. array $localColumnNames,
  382. array $foreignColumnNames,
  383. array $options = []
  384. ) {
  385. if ($foreignTable instanceof Table) {
  386. foreach ($foreignColumnNames as $columnName) {
  387. if (! $foreignTable->hasColumn($columnName)) {
  388. throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
  389. }
  390. }
  391. }
  392. foreach ($localColumnNames as $columnName) {
  393. if (! $this->hasColumn($columnName)) {
  394. throw SchemaException::columnDoesNotExist($columnName, $this->_name);
  395. }
  396. }
  397. $constraint = new ForeignKeyConstraint(
  398. $localColumnNames,
  399. $foreignTable,
  400. $foreignColumnNames,
  401. $name,
  402. $options
  403. );
  404. $this->_addForeignKeyConstraint($constraint);
  405. return $this;
  406. }
  407. /**
  408. * @param string $name
  409. * @param mixed $value
  410. *
  411. * @return self
  412. */
  413. public function addOption($name, $value)
  414. {
  415. $this->_options[$name] = $value;
  416. return $this;
  417. }
  418. /**
  419. * @return void
  420. *
  421. * @throws SchemaException
  422. */
  423. protected function _addColumn(Column $column)
  424. {
  425. $columnName = $column->getName();
  426. $columnName = $this->normalizeIdentifier($columnName);
  427. if (isset($this->_columns[$columnName])) {
  428. throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
  429. }
  430. $this->_columns[$columnName] = $column;
  431. }
  432. /**
  433. * Adds an index to the table.
  434. *
  435. * @return self
  436. *
  437. * @throws SchemaException
  438. */
  439. protected function _addIndex(Index $indexCandidate)
  440. {
  441. $indexName = $indexCandidate->getName();
  442. $indexName = $this->normalizeIdentifier($indexName);
  443. $replacedImplicitIndexes = [];
  444. foreach ($this->implicitIndexes as $name => $implicitIndex) {
  445. if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
  446. continue;
  447. }
  448. $replacedImplicitIndexes[] = $name;
  449. }
  450. if (
  451. (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
  452. ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
  453. ) {
  454. throw SchemaException::indexAlreadyExists($indexName, $this->_name);
  455. }
  456. foreach ($replacedImplicitIndexes as $name) {
  457. unset($this->_indexes[$name], $this->implicitIndexes[$name]);
  458. }
  459. if ($indexCandidate->isPrimary()) {
  460. $this->_primaryKeyName = $indexName;
  461. }
  462. $this->_indexes[$indexName] = $indexCandidate;
  463. return $this;
  464. }
  465. /**
  466. * @return void
  467. */
  468. protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
  469. {
  470. $constraint->setLocalTable($this);
  471. if (strlen($constraint->getName())) {
  472. $name = $constraint->getName();
  473. } else {
  474. $name = $this->_generateIdentifierName(
  475. array_merge((array) $this->getName(), $constraint->getLocalColumns()),
  476. 'fk',
  477. $this->_getMaxIdentifierLength()
  478. );
  479. }
  480. $name = $this->normalizeIdentifier($name);
  481. $this->_fkConstraints[$name] = $constraint;
  482. /* Add an implicit index (defined by the DBAL) on the foreign key
  483. columns. If there is already a user-defined index that fulfills these
  484. requirements drop the request. In the case of __construct() calling
  485. this method during hydration from schema-details, all the explicitly
  486. added indexes lead to duplicates. This creates computation overhead in
  487. this case, however no duplicate indexes are ever added (based on
  488. columns). */
  489. $indexName = $this->_generateIdentifierName(
  490. array_merge([$this->getName()], $constraint->getColumns()),
  491. 'idx',
  492. $this->_getMaxIdentifierLength()
  493. );
  494. $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
  495. foreach ($this->_indexes as $existingIndex) {
  496. if ($indexCandidate->isFullfilledBy($existingIndex)) {
  497. return;
  498. }
  499. }
  500. $this->_addIndex($indexCandidate);
  501. $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
  502. }
  503. /**
  504. * Returns whether this table has a foreign key constraint with the given name.
  505. *
  506. * @param string $name
  507. *
  508. * @return bool
  509. */
  510. public function hasForeignKey($name)
  511. {
  512. $name = $this->normalizeIdentifier($name);
  513. return isset($this->_fkConstraints[$name]);
  514. }
  515. /**
  516. * Returns the foreign key constraint with the given name.
  517. *
  518. * @param string $name The constraint name.
  519. *
  520. * @return ForeignKeyConstraint
  521. *
  522. * @throws SchemaException If the foreign key does not exist.
  523. */
  524. public function getForeignKey($name)
  525. {
  526. $name = $this->normalizeIdentifier($name);
  527. if (! $this->hasForeignKey($name)) {
  528. throw SchemaException::foreignKeyDoesNotExist($name, $this->_name);
  529. }
  530. return $this->_fkConstraints[$name];
  531. }
  532. /**
  533. * Removes the foreign key constraint with the given name.
  534. *
  535. * @param string $name The constraint name.
  536. *
  537. * @return void
  538. *
  539. * @throws SchemaException
  540. */
  541. public function removeForeignKey($name)
  542. {
  543. $name = $this->normalizeIdentifier($name);
  544. if (! $this->hasForeignKey($name)) {
  545. throw SchemaException::foreignKeyDoesNotExist($name, $this->_name);
  546. }
  547. unset($this->_fkConstraints[$name]);
  548. }
  549. /**
  550. * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
  551. *
  552. * @return Column[]
  553. */
  554. public function getColumns()
  555. {
  556. $primaryKey = $this->getPrimaryKey();
  557. $primaryKeyColumns = [];
  558. if ($primaryKey !== null) {
  559. $primaryKeyColumns = $this->filterColumns($primaryKey->getColumns());
  560. }
  561. return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
  562. }
  563. /**
  564. * Returns foreign key columns
  565. *
  566. * @return Column[]
  567. */
  568. private function getForeignKeyColumns()
  569. {
  570. $foreignKeyColumns = [];
  571. foreach ($this->getForeignKeys() as $foreignKey) {
  572. $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
  573. }
  574. return $this->filterColumns($foreignKeyColumns);
  575. }
  576. /**
  577. * Returns only columns that have specified names
  578. *
  579. * @param string[] $columnNames
  580. *
  581. * @return Column[]
  582. */
  583. private function filterColumns(array $columnNames)
  584. {
  585. return array_filter($this->_columns, static function (string $columnName) use ($columnNames) {
  586. return in_array($columnName, $columnNames, true);
  587. }, ARRAY_FILTER_USE_KEY);
  588. }
  589. /**
  590. * Returns whether this table has a Column with the given name.
  591. *
  592. * @param string $name The column name.
  593. *
  594. * @return bool
  595. */
  596. public function hasColumn($name)
  597. {
  598. $name = $this->normalizeIdentifier($name);
  599. return isset($this->_columns[$name]);
  600. }
  601. /**
  602. * Returns the Column with the given name.
  603. *
  604. * @param string $name The column name.
  605. *
  606. * @return Column
  607. *
  608. * @throws SchemaException If the column does not exist.
  609. */
  610. public function getColumn($name)
  611. {
  612. $name = $this->normalizeIdentifier($name);
  613. if (! $this->hasColumn($name)) {
  614. throw SchemaException::columnDoesNotExist($name, $this->_name);
  615. }
  616. return $this->_columns[$name];
  617. }
  618. /**
  619. * Returns the primary key.
  620. *
  621. * @return Index|null The primary key, or null if this Table has no primary key.
  622. */
  623. public function getPrimaryKey()
  624. {
  625. if ($this->_primaryKeyName !== false) {
  626. return $this->getIndex($this->_primaryKeyName);
  627. }
  628. return null;
  629. }
  630. /**
  631. * Returns the primary key columns.
  632. *
  633. * @return string[]
  634. *
  635. * @throws Exception
  636. */
  637. public function getPrimaryKeyColumns()
  638. {
  639. $primaryKey = $this->getPrimaryKey();
  640. if ($primaryKey === null) {
  641. throw new Exception('Table ' . $this->getName() . ' has no primary key.');
  642. }
  643. return $primaryKey->getColumns();
  644. }
  645. /**
  646. * Returns whether this table has a primary key.
  647. *
  648. * @return bool
  649. */
  650. public function hasPrimaryKey()
  651. {
  652. return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
  653. }
  654. /**
  655. * Returns whether this table has an Index with the given name.
  656. *
  657. * @param string $name The index name.
  658. *
  659. * @return bool
  660. */
  661. public function hasIndex($name)
  662. {
  663. $name = $this->normalizeIdentifier($name);
  664. return isset($this->_indexes[$name]);
  665. }
  666. /**
  667. * Returns the Index with the given name.
  668. *
  669. * @param string $name The index name.
  670. *
  671. * @return Index
  672. *
  673. * @throws SchemaException If the index does not exist.
  674. */
  675. public function getIndex($name)
  676. {
  677. $name = $this->normalizeIdentifier($name);
  678. if (! $this->hasIndex($name)) {
  679. throw SchemaException::indexDoesNotExist($name, $this->_name);
  680. }
  681. return $this->_indexes[$name];
  682. }
  683. /**
  684. * @return Index[]
  685. */
  686. public function getIndexes()
  687. {
  688. return $this->_indexes;
  689. }
  690. /**
  691. * Returns the foreign key constraints.
  692. *
  693. * @return ForeignKeyConstraint[]
  694. */
  695. public function getForeignKeys()
  696. {
  697. return $this->_fkConstraints;
  698. }
  699. /**
  700. * @param string $name
  701. *
  702. * @return bool
  703. */
  704. public function hasOption($name)
  705. {
  706. return isset($this->_options[$name]);
  707. }
  708. /**
  709. * @param string $name
  710. *
  711. * @return mixed
  712. */
  713. public function getOption($name)
  714. {
  715. return $this->_options[$name];
  716. }
  717. /**
  718. * @return mixed[]
  719. */
  720. public function getOptions()
  721. {
  722. return $this->_options;
  723. }
  724. /**
  725. * @return void
  726. */
  727. public function visit(Visitor $visitor)
  728. {
  729. $visitor->acceptTable($this);
  730. foreach ($this->getColumns() as $column) {
  731. $visitor->acceptColumn($this, $column);
  732. }
  733. foreach ($this->getIndexes() as $index) {
  734. $visitor->acceptIndex($this, $index);
  735. }
  736. foreach ($this->getForeignKeys() as $constraint) {
  737. $visitor->acceptForeignKey($this, $constraint);
  738. }
  739. }
  740. /**
  741. * Clone of a Table triggers a deep clone of all affected assets.
  742. *
  743. * @return void
  744. */
  745. public function __clone()
  746. {
  747. foreach ($this->_columns as $k => $column) {
  748. $this->_columns[$k] = clone $column;
  749. }
  750. foreach ($this->_indexes as $k => $index) {
  751. $this->_indexes[$k] = clone $index;
  752. }
  753. foreach ($this->_fkConstraints as $k => $fk) {
  754. $this->_fkConstraints[$k] = clone $fk;
  755. $this->_fkConstraints[$k]->setLocalTable($this);
  756. }
  757. }
  758. /**
  759. * Normalizes a given identifier.
  760. *
  761. * Trims quotes and lowercases the given identifier.
  762. *
  763. * @param string|null $identifier The identifier to normalize.
  764. *
  765. * @return string The normalized identifier.
  766. */
  767. private function normalizeIdentifier($identifier)
  768. {
  769. if ($identifier === null) {
  770. return '';
  771. }
  772. return $this->trimQuotes(strtolower($identifier));
  773. }
  774. public function setComment(?string $comment): self
  775. {
  776. // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options.
  777. $this->addOption('comment', $comment);
  778. return $this;
  779. }
  780. public function getComment(): ?string
  781. {
  782. return $this->_options['comment'] ?? null;
  783. }
  784. }