PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/doctrine-dbal/lib/Doctrine/DBAL/Schema/Table.php

https://github.com/casoetan/ServerGroveLiveChat
PHP | 629 lines | 316 code | 80 blank | 233 comment | 32 complexity | 7937034e9296a73db2fc0ef6e11eaac1 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, ISC, BSD-3-Clause
  1. <?php
  2. /*
  3. * $Id$
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information, see
  19. * <http://www.doctrine-project.org>.
  20. */
  21. namespace Doctrine\DBAL\Schema;
  22. use Doctrine\DBAL\Types\Type;
  23. use Doctrine\DBAL\Schema\Visitor\Visitor;
  24. use Doctrine\DBAL\DBALException;
  25. /**
  26. * Object Representation of a table
  27. *
  28. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  29. * @link www.doctrine-project.org
  30. * @since 2.0
  31. * @version $Revision$
  32. * @author Benjamin Eberlei <kontakt@beberlei.de>
  33. */
  34. class Table extends AbstractAsset
  35. {
  36. /**
  37. * @var string
  38. */
  39. protected $_name = null;
  40. /**
  41. * @var array
  42. */
  43. protected $_columns = array();
  44. /**
  45. * @var array
  46. */
  47. protected $_indexes = array();
  48. /**
  49. * @var string
  50. */
  51. protected $_primaryKeyName = false;
  52. /**
  53. * @var array
  54. */
  55. protected $_fkConstraints = array();
  56. /**
  57. * @var array
  58. */
  59. protected $_options = array();
  60. /**
  61. * @var SchemaConfig
  62. */
  63. protected $_schemaConfig = null;
  64. /**
  65. *
  66. * @param string $tableName
  67. * @param array $columns
  68. * @param array $indexes
  69. * @param array $fkConstraints
  70. * @param int $idGeneratorType
  71. * @param array $options
  72. */
  73. public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array())
  74. {
  75. if (strlen($tableName) == 0) {
  76. throw DBALException::invalidTableName($tableName);
  77. }
  78. $this->_setName($tableName);
  79. $this->_idGeneratorType = $idGeneratorType;
  80. foreach ($columns AS $column) {
  81. $this->_addColumn($column);
  82. }
  83. foreach ($indexes AS $idx) {
  84. $this->_addIndex($idx);
  85. }
  86. foreach ($fkConstraints AS $constraint) {
  87. $this->_addForeignKeyConstraint($constraint);
  88. }
  89. $this->_options = $options;
  90. }
  91. /**
  92. * @param SchemaConfig $schemaConfig
  93. */
  94. public function setSchemaConfig(SchemaConfig $schemaConfig)
  95. {
  96. $this->_schemaConfig = $schemaConfig;
  97. }
  98. /**
  99. * @return int
  100. */
  101. protected function _getMaxIdentifierLength()
  102. {
  103. if ($this->_schemaConfig instanceof SchemaConfig) {
  104. return $this->_schemaConfig->getMaxIdentifierLength();
  105. } else {
  106. return 63;
  107. }
  108. }
  109. /**
  110. * Set Primary Key
  111. *
  112. * @param array $columns
  113. * @param string $indexName
  114. * @return Table
  115. */
  116. public function setPrimaryKey(array $columns, $indexName = false)
  117. {
  118. $primaryKey = $this->_createIndex($columns, $indexName ?: "primary", true, true);
  119. foreach ($columns AS $columnName) {
  120. $column = $this->getColumn($columnName);
  121. $column->setNotnull(true);
  122. }
  123. return $primaryKey;
  124. }
  125. /**
  126. * @param array $columnNames
  127. * @param string $indexName
  128. * @return Table
  129. */
  130. public function addIndex(array $columnNames, $indexName = null)
  131. {
  132. if($indexName == null) {
  133. $indexName = $this->_generateIdentifierName(
  134. array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength()
  135. );
  136. }
  137. return $this->_createIndex($columnNames, $indexName, false, false);
  138. }
  139. /**
  140. *
  141. * @param array $columnNames
  142. * @param string $indexName
  143. * @return Table
  144. */
  145. public function addUniqueIndex(array $columnNames, $indexName = null)
  146. {
  147. if ($indexName == null) {
  148. $indexName = $this->_generateIdentifierName(
  149. array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength()
  150. );
  151. }
  152. return $this->_createIndex($columnNames, $indexName, true, false);
  153. }
  154. /**
  155. * Check if an index begins in the order of the given columns.
  156. *
  157. * @param array $columnsNames
  158. * @return bool
  159. */
  160. public function columnsAreIndexed(array $columnsNames)
  161. {
  162. foreach ($this->getIndexes() AS $index) {
  163. /* @var $index Index */
  164. if ($index->spansColumns($columnsNames)) {
  165. return true;
  166. }
  167. }
  168. return false;
  169. }
  170. /**
  171. *
  172. * @param array $columnNames
  173. * @param string $indexName
  174. * @param bool $isUnique
  175. * @param bool $isPrimary
  176. * @return Table
  177. */
  178. private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary)
  179. {
  180. if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) {
  181. throw SchemaException::indexNameInvalid($indexName);
  182. }
  183. foreach ($columnNames AS $columnName => $indexColOptions) {
  184. if (is_numeric($columnName) && is_string($indexColOptions)) {
  185. $columnName = $indexColOptions;
  186. }
  187. if ( ! $this->hasColumn($columnName)) {
  188. throw SchemaException::columnDoesNotExist($columnName, $this->_name);
  189. }
  190. }
  191. $this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary));
  192. return $this;
  193. }
  194. /**
  195. * @param string $columnName
  196. * @param string $columnType
  197. * @param array $options
  198. * @return Column
  199. */
  200. public function addColumn($columnName, $typeName, array $options=array())
  201. {
  202. $column = new Column($columnName, Type::getType($typeName), $options);
  203. $this->_addColumn($column);
  204. return $column;
  205. }
  206. /**
  207. * Rename Column
  208. *
  209. * @param string $oldColumnName
  210. * @param string $newColumnName
  211. * @return Table
  212. */
  213. public function renameColumn($oldColumnName, $newColumnName)
  214. {
  215. $column = $this->getColumn($oldColumnName);
  216. $this->dropColumn($oldColumnName);
  217. $column->_setName($newColumnName);
  218. return $this;
  219. }
  220. /**
  221. * Change Column Details
  222. *
  223. * @param string $columnName
  224. * @param array $options
  225. * @return Table
  226. */
  227. public function changeColumn($columnName, array $options)
  228. {
  229. $column = $this->getColumn($columnName);
  230. $column->setOptions($options);
  231. return $this;
  232. }
  233. /**
  234. * Drop Column from Table
  235. *
  236. * @param string $columnName
  237. * @return Table
  238. */
  239. public function dropColumn($columnName)
  240. {
  241. $columnName = strtolower($columnName);
  242. $column = $this->getColumn($columnName);
  243. unset($this->_columns[$columnName]);
  244. return $this;
  245. }
  246. /**
  247. * Add a foreign key constraint
  248. *
  249. * Name is inferred from the local columns
  250. *
  251. * @param Table $foreignTable
  252. * @param array $localColumns
  253. * @param array $foreignColumns
  254. * @param array $options
  255. * @return Table
  256. */
  257. public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array())
  258. {
  259. $name = $this->_generateIdentifierName(array_merge((array)$this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength());
  260. return $this->addNamedForeignKeyConstraint($name, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
  261. }
  262. /**
  263. * Add a foreign key constraint
  264. *
  265. * Name is to be generated by the database itsself.
  266. *
  267. * @param Table $foreignTable
  268. * @param array $localColumns
  269. * @param array $foreignColumns
  270. * @param array $options
  271. * @return Table
  272. */
  273. public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array())
  274. {
  275. return $this->addNamedForeignKeyConstraint(null, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
  276. }
  277. /**
  278. * Add a foreign key constraint with a given name
  279. *
  280. * @param string $name
  281. * @param Table $foreignTable
  282. * @param array $localColumns
  283. * @param array $foreignColumns
  284. * @param array $options
  285. * @return Table
  286. */
  287. public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array())
  288. {
  289. if ($foreignTable instanceof Table) {
  290. $foreignTableName = $foreignTable->getName();
  291. foreach ($foreignColumnNames AS $columnName) {
  292. if ( ! $foreignTable->hasColumn($columnName)) {
  293. throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
  294. }
  295. }
  296. } else {
  297. $foreignTableName = $foreignTable;
  298. }
  299. foreach ($localColumnNames AS $columnName) {
  300. if ( ! $this->hasColumn($columnName)) {
  301. throw SchemaException::columnDoesNotExist($columnName, $this->_name);
  302. }
  303. }
  304. $constraint = new ForeignKeyConstraint(
  305. $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options
  306. );
  307. $this->_addForeignKeyConstraint($constraint);
  308. return $this;
  309. }
  310. /**
  311. * @param string $name
  312. * @param string $value
  313. * @return Table
  314. */
  315. public function addOption($name, $value)
  316. {
  317. $this->_options[$name] = $value;
  318. return $this;
  319. }
  320. /**
  321. * @param Column $column
  322. */
  323. protected function _addColumn(Column $column)
  324. {
  325. $columnName = $column->getName();
  326. $columnName = strtolower($columnName);
  327. if (isset($this->_columns[$columnName])) {
  328. throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
  329. }
  330. $this->_columns[$columnName] = $column;
  331. }
  332. /**
  333. * Add index to table
  334. *
  335. * @param Index $indexCandidate
  336. * @return Table
  337. */
  338. protected function _addIndex(Index $indexCandidate)
  339. {
  340. // check for duplicates
  341. foreach ($this->_indexes AS $existingIndex) {
  342. if ($indexCandidate->isFullfilledBy($existingIndex)) {
  343. return $this;
  344. }
  345. }
  346. $indexName = $indexCandidate->getName();
  347. $indexName = strtolower($indexName);
  348. if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $indexCandidate->isPrimary())) {
  349. throw SchemaException::indexAlreadyExists($indexName, $this->_name);
  350. }
  351. // remove overruled indexes
  352. foreach ($this->_indexes AS $idxKey => $existingIndex) {
  353. if ($indexCandidate->overrules($existingIndex)) {
  354. unset($this->_indexes[$idxKey]);
  355. }
  356. }
  357. if ($indexCandidate->isPrimary()) {
  358. $this->_primaryKeyName = $indexName;
  359. }
  360. $this->_indexes[$indexName] = $indexCandidate;
  361. return $this;
  362. }
  363. /**
  364. * @param ForeignKeyConstraint $constraint
  365. */
  366. protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
  367. {
  368. $constraint->setLocalTable($this);
  369. if(strlen($constraint->getName())) {
  370. $name = $constraint->getName();
  371. } else {
  372. $name = $this->_generateIdentifierName(
  373. array_merge((array)$this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength()
  374. );
  375. }
  376. $name = strtolower($name);
  377. $this->_fkConstraints[$name] = $constraint;
  378. // add an explicit index on the foreign key columns. If there is already an index that fullfils this requirements drop the request.
  379. // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
  380. // lead to duplicates. This creates compuation overhead in this case, however no duplicate indexes are ever added (based on columns).
  381. $this->addIndex($constraint->getColumns());
  382. }
  383. /**
  384. * Does Table have a foreign key constraint with the given name?
  385. * *
  386. * @param string $constraintName
  387. * @return bool
  388. */
  389. public function hasForeignKey($constraintName)
  390. {
  391. $constraintName = strtolower($constraintName);
  392. return isset($this->_fkConstraints[$constraintName]);
  393. }
  394. /**
  395. * @param string $constraintName
  396. * @return ForeignKeyConstraint
  397. */
  398. public function getForeignKey($constraintName)
  399. {
  400. $constraintName = strtolower($constraintName);
  401. if(!$this->hasForeignKey($constraintName)) {
  402. throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
  403. }
  404. return $this->_fkConstraints[$constraintName];
  405. }
  406. /**
  407. * @return Column[]
  408. */
  409. public function getColumns()
  410. {
  411. $columns = $this->_columns;
  412. $pkCols = array();
  413. $fkCols = array();
  414. if ($this->hasIndex($this->_primaryKeyName)) {
  415. $pkCols = $this->getPrimaryKey()->getColumns();
  416. }
  417. foreach ($this->getForeignKeys() AS $fk) {
  418. /* @var $fk ForeignKeyConstraint */
  419. $fkCols = array_merge($fkCols, $fk->getColumns());
  420. }
  421. $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
  422. uksort($columns, function($a, $b) use($colNames) {
  423. return (array_search($a, $colNames) >= array_search($b, $colNames));
  424. });
  425. return $columns;
  426. }
  427. /**
  428. * Does this table have a column with the given name?
  429. *
  430. * @param string $columnName
  431. * @return bool
  432. */
  433. public function hasColumn($columnName)
  434. {
  435. $columnName = strtolower($columnName);
  436. return isset($this->_columns[$columnName]);
  437. }
  438. /**
  439. * Get a column instance
  440. *
  441. * @param string $columnName
  442. * @return Column
  443. */
  444. public function getColumn($columnName)
  445. {
  446. $columnName = strtolower($columnName);
  447. if (!$this->hasColumn($columnName)) {
  448. throw SchemaException::columnDoesNotExist($columnName, $this->_name);
  449. }
  450. return $this->_columns[$columnName];
  451. }
  452. /**
  453. * @return Index
  454. */
  455. public function getPrimaryKey()
  456. {
  457. return $this->getIndex($this->_primaryKeyName);
  458. }
  459. /**
  460. * Check if this table has a primary key.
  461. *
  462. * @return bool
  463. */
  464. public function hasPrimaryKey()
  465. {
  466. return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName));
  467. }
  468. /**
  469. * @param string $indexName
  470. * @return bool
  471. */
  472. public function hasIndex($indexName)
  473. {
  474. $indexName = strtolower($indexName);
  475. return (isset($this->_indexes[$indexName]));
  476. }
  477. /**
  478. * @param string $indexName
  479. * @return Index
  480. */
  481. public function getIndex($indexName)
  482. {
  483. $indexName = strtolower($indexName);
  484. if (!$this->hasIndex($indexName)) {
  485. throw SchemaException::indexDoesNotExist($indexName, $this->_name);
  486. }
  487. return $this->_indexes[$indexName];
  488. }
  489. /**
  490. * @return array
  491. */
  492. public function getIndexes()
  493. {
  494. return $this->_indexes;
  495. }
  496. /**
  497. * Get Constraints
  498. *
  499. * @return array
  500. */
  501. public function getForeignKeys()
  502. {
  503. return $this->_fkConstraints;
  504. }
  505. public function hasOption($name)
  506. {
  507. return isset($this->_options[$name]);
  508. }
  509. public function getOption($name)
  510. {
  511. return $this->_options[$name];
  512. }
  513. public function getOptions()
  514. {
  515. return $this->_options;
  516. }
  517. /**
  518. * @param Visitor $visitor
  519. */
  520. public function visit(Visitor $visitor)
  521. {
  522. $visitor->acceptTable($this);
  523. foreach ($this->getColumns() AS $column) {
  524. $visitor->acceptColumn($this, $column);
  525. }
  526. foreach ($this->getIndexes() AS $index) {
  527. $visitor->acceptIndex($this, $index);
  528. }
  529. foreach ($this->getForeignKeys() AS $constraint) {
  530. $visitor->acceptForeignKey($this, $constraint);
  531. }
  532. }
  533. /**
  534. * Clone of a Table triggers a deep clone of all affected assets
  535. */
  536. public function __clone()
  537. {
  538. foreach ($this->_columns AS $k => $column) {
  539. $this->_columns[$k] = clone $column;
  540. }
  541. foreach ($this->_indexes AS $k => $index) {
  542. $this->_indexes[$k] = clone $index;
  543. }
  544. foreach ($this->_fkConstraints AS $k => $fk) {
  545. $this->_fkConstraints[$k] = clone $fk;
  546. $this->_fkConstraints[$k]->setLocalTable($this);
  547. }
  548. }
  549. }