Table.php 23 KB

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