vendor/doctrine-dbal/lib/Doctrine/DBAL/Schema/Table.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     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 
       
    22 namespace Doctrine\DBAL\Schema;
       
    23 
       
    24 use Doctrine\DBAL\Types\Type;
       
    25 use Doctrine\DBAL\Schema\Visitor\Visitor;
       
    26 use Doctrine\DBAL\DBALException;
       
    27 
       
    28 /**
       
    29  * Object Representation of a table
       
    30  *
       
    31  * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
       
    32  * @link    www.doctrine-project.org
       
    33  * @since   2.0
       
    34  * @version $Revision$
       
    35  * @author  Benjamin Eberlei <kontakt@beberlei.de>
       
    36  */
       
    37 class Table extends AbstractAsset
       
    38 {
       
    39     /**
       
    40      * @var string
       
    41      */
       
    42     protected $_name = null;
       
    43 
       
    44     /**
       
    45      * @var array
       
    46      */
       
    47     protected $_columns = array();
       
    48 
       
    49     /**
       
    50      * @var array
       
    51      */
       
    52     protected $_indexes = array();
       
    53 
       
    54     /**
       
    55      * @var string
       
    56      */
       
    57     protected $_primaryKeyName = false;
       
    58 
       
    59     /**
       
    60      * @var array
       
    61      */
       
    62     protected $_fkConstraints = array();
       
    63 
       
    64     /**
       
    65      * @var array
       
    66      */
       
    67     protected $_options = array();
       
    68 
       
    69     /**
       
    70      * @var SchemaConfig
       
    71      */
       
    72     protected $_schemaConfig = null;
       
    73 
       
    74     /**
       
    75      *
       
    76      * @param string $tableName
       
    77      * @param array $columns
       
    78      * @param array $indexes
       
    79      * @param array $fkConstraints
       
    80      * @param int $idGeneratorType
       
    81      * @param array $options
       
    82      */
       
    83     public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array())
       
    84     {
       
    85         if (strlen($tableName) == 0) {
       
    86             throw DBALException::invalidTableName($tableName);
       
    87         }
       
    88 
       
    89         $this->_setName($tableName);
       
    90         $this->_idGeneratorType = $idGeneratorType;
       
    91         
       
    92         foreach ($columns AS $column) {
       
    93             $this->_addColumn($column);
       
    94         }
       
    95         
       
    96         foreach ($indexes AS $idx) {
       
    97             $this->_addIndex($idx);
       
    98         }
       
    99 
       
   100         foreach ($fkConstraints AS $constraint) {
       
   101             $this->_addForeignKeyConstraint($constraint);
       
   102         }
       
   103 
       
   104         $this->_options = $options;
       
   105     }
       
   106 
       
   107     /**
       
   108      * @param SchemaConfig $schemaConfig
       
   109      */
       
   110     public function setSchemaConfig(SchemaConfig $schemaConfig)
       
   111     {
       
   112         $this->_schemaConfig = $schemaConfig;
       
   113     }
       
   114 
       
   115     /**
       
   116      * @return int
       
   117      */
       
   118     protected function _getMaxIdentifierLength()
       
   119     {
       
   120         if ($this->_schemaConfig instanceof SchemaConfig) {
       
   121             return $this->_schemaConfig->getMaxIdentifierLength();
       
   122         } else {
       
   123             return 63;
       
   124         }
       
   125     }
       
   126 
       
   127     /**
       
   128      * Set Primary Key
       
   129      *
       
   130      * @param array $columns
       
   131      * @param string $indexName
       
   132      * @return Table
       
   133      */
       
   134     public function setPrimaryKey(array $columns, $indexName = false)
       
   135     {
       
   136         $primaryKey = $this->_createIndex($columns, $indexName ?: "primary", true, true);
       
   137 
       
   138         foreach ($columns AS $columnName) {
       
   139             $column = $this->getColumn($columnName);
       
   140             $column->setNotnull(true);
       
   141         }
       
   142 
       
   143         return $primaryKey;
       
   144     }
       
   145 
       
   146     /**
       
   147      * @param array $columnNames
       
   148      * @param string $indexName
       
   149      * @return Table
       
   150      */
       
   151     public function addIndex(array $columnNames, $indexName = null)
       
   152     {
       
   153         if($indexName == null) {
       
   154             $indexName = $this->_generateIdentifierName(
       
   155                 array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength()
       
   156             );
       
   157         }
       
   158 
       
   159         return $this->_createIndex($columnNames, $indexName, false, false);
       
   160     }
       
   161 
       
   162     /**
       
   163      *
       
   164      * @param array $columnNames
       
   165      * @param string $indexName
       
   166      * @return Table
       
   167      */
       
   168     public function addUniqueIndex(array $columnNames, $indexName = null)
       
   169     {
       
   170         if ($indexName == null) {
       
   171             $indexName = $this->_generateIdentifierName(
       
   172                 array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength()
       
   173             );
       
   174         }
       
   175 
       
   176         return $this->_createIndex($columnNames, $indexName, true, false);
       
   177     }
       
   178 
       
   179     /**
       
   180      * Check if an index begins in the order of the given columns.
       
   181      *
       
   182      * @param  array $columnsNames
       
   183      * @return bool
       
   184      */
       
   185     public function columnsAreIndexed(array $columnsNames)
       
   186     {
       
   187         foreach ($this->getIndexes() AS $index) {
       
   188             /* @var $index Index */
       
   189             if ($index->spansColumns($columnsNames)) {
       
   190                 return true;
       
   191             }
       
   192         }
       
   193         return false;
       
   194     }
       
   195 
       
   196     /**
       
   197      *
       
   198      * @param array $columnNames
       
   199      * @param string $indexName
       
   200      * @param bool $isUnique
       
   201      * @param bool $isPrimary
       
   202      * @return Table
       
   203      */
       
   204     private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary)
       
   205     {
       
   206         if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) {
       
   207             throw SchemaException::indexNameInvalid($indexName);
       
   208         }
       
   209 
       
   210         foreach ($columnNames AS $columnName => $indexColOptions) {
       
   211             if (is_numeric($columnName) && is_string($indexColOptions)) {
       
   212                 $columnName = $indexColOptions;
       
   213             }
       
   214 
       
   215             if ( ! $this->hasColumn($columnName)) {
       
   216                 throw SchemaException::columnDoesNotExist($columnName, $this->_name);
       
   217             }
       
   218         }
       
   219         $this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary));
       
   220         return $this;
       
   221     }
       
   222 
       
   223     /**
       
   224      * @param string $columnName
       
   225      * @param string $columnType
       
   226      * @param array $options
       
   227      * @return Column
       
   228      */
       
   229     public function addColumn($columnName, $typeName, array $options=array())
       
   230     {
       
   231         $column = new Column($columnName, Type::getType($typeName), $options);
       
   232 
       
   233         $this->_addColumn($column);
       
   234         return $column;
       
   235     }
       
   236 
       
   237     /**
       
   238      * Rename Column
       
   239      *
       
   240      * @param string $oldColumnName
       
   241      * @param string $newColumnName
       
   242      * @return Table
       
   243      */
       
   244     public function renameColumn($oldColumnName, $newColumnName)
       
   245     {
       
   246         $column = $this->getColumn($oldColumnName);
       
   247         $this->dropColumn($oldColumnName);
       
   248 
       
   249         $column->_setName($newColumnName);
       
   250         return $this;
       
   251     }
       
   252 
       
   253     /**
       
   254      * Change Column Details
       
   255      * 
       
   256      * @param string $columnName
       
   257      * @param array $options
       
   258      * @return Table
       
   259      */
       
   260     public function changeColumn($columnName, array $options)
       
   261     {
       
   262         $column = $this->getColumn($columnName);
       
   263         $column->setOptions($options);
       
   264         return $this;
       
   265     }
       
   266 
       
   267     /**
       
   268      * Drop Column from Table
       
   269      * 
       
   270      * @param string $columnName
       
   271      * @return Table
       
   272      */
       
   273     public function dropColumn($columnName)
       
   274     {
       
   275         $columnName = strtolower($columnName);
       
   276         $column = $this->getColumn($columnName);
       
   277         unset($this->_columns[$columnName]);
       
   278         return $this;
       
   279     }
       
   280 
       
   281 
       
   282     /**
       
   283      * Add a foreign key constraint
       
   284      *
       
   285      * Name is inferred from the local columns
       
   286      *
       
   287      * @param Table $foreignTable
       
   288      * @param array $localColumns
       
   289      * @param array $foreignColumns
       
   290      * @param array $options
       
   291      * @param string $constraintName
       
   292      * @return Table
       
   293      */
       
   294     public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array(), $constraintName = null)
       
   295     {
       
   296         $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array)$this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength());
       
   297         return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
       
   298     }
       
   299 
       
   300     /**
       
   301      * Add a foreign key constraint
       
   302      *
       
   303      * Name is to be generated by the database itsself.
       
   304      *
       
   305      * @deprecated Use {@link addForeignKeyConstraint}
       
   306      * @param Table $foreignTable
       
   307      * @param array $localColumns
       
   308      * @param array $foreignColumns
       
   309      * @param array $options
       
   310      * @return Table
       
   311      */
       
   312     public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array())
       
   313     {
       
   314         return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
       
   315     }
       
   316 
       
   317     /**
       
   318      * Add a foreign key constraint with a given name
       
   319      *
       
   320      * @deprecated Use {@link addForeignKeyConstraint}
       
   321      * @param string $name
       
   322      * @param Table $foreignTable
       
   323      * @param array $localColumns
       
   324      * @param array $foreignColumns
       
   325      * @param array $options
       
   326      * @return Table
       
   327      */
       
   328     public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array())
       
   329     {
       
   330         if ($foreignTable instanceof Table) {
       
   331             $foreignTableName = $foreignTable->getName();
       
   332 
       
   333             foreach ($foreignColumnNames AS $columnName) {
       
   334                 if ( ! $foreignTable->hasColumn($columnName)) {
       
   335                     throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
       
   336                 }
       
   337             }
       
   338         } else {
       
   339             $foreignTableName = $foreignTable;
       
   340         }
       
   341 
       
   342         foreach ($localColumnNames AS $columnName) {
       
   343             if ( ! $this->hasColumn($columnName)) {
       
   344                 throw SchemaException::columnDoesNotExist($columnName, $this->_name);
       
   345             }
       
   346         }
       
   347         
       
   348         $constraint = new ForeignKeyConstraint(
       
   349             $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options
       
   350         );
       
   351         $this->_addForeignKeyConstraint($constraint);
       
   352 
       
   353         return $this;
       
   354     }
       
   355 
       
   356     /**
       
   357      * @param string $name
       
   358      * @param string $value
       
   359      * @return Table
       
   360      */
       
   361     public function addOption($name, $value)
       
   362     {
       
   363         $this->_options[$name] = $value;
       
   364         return $this;
       
   365     }
       
   366 
       
   367     /**
       
   368      * @param Column $column
       
   369      */
       
   370     protected function _addColumn(Column $column)
       
   371     {
       
   372         $columnName = $column->getName();
       
   373         $columnName = strtolower($columnName);
       
   374 
       
   375         if (isset($this->_columns[$columnName])) {
       
   376             throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
       
   377         }
       
   378 
       
   379         $this->_columns[$columnName] = $column;
       
   380     }
       
   381 
       
   382     /**
       
   383      * Add index to table
       
   384      * 
       
   385      * @param Index $indexCandidate
       
   386      * @return Table
       
   387      */
       
   388     protected function _addIndex(Index $indexCandidate)
       
   389     {
       
   390         // check for duplicates
       
   391         foreach ($this->_indexes AS $existingIndex) {
       
   392             if ($indexCandidate->isFullfilledBy($existingIndex)) {
       
   393                 return $this;
       
   394             }
       
   395         }
       
   396 
       
   397         $indexName = $indexCandidate->getName();
       
   398         $indexName = strtolower($indexName);
       
   399 
       
   400         if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $indexCandidate->isPrimary())) {
       
   401             throw SchemaException::indexAlreadyExists($indexName, $this->_name);
       
   402         }
       
   403 
       
   404         // remove overruled indexes
       
   405         foreach ($this->_indexes AS $idxKey => $existingIndex) {
       
   406             if ($indexCandidate->overrules($existingIndex)) {
       
   407                 unset($this->_indexes[$idxKey]);
       
   408             }
       
   409         }
       
   410 
       
   411         if ($indexCandidate->isPrimary()) {
       
   412             $this->_primaryKeyName = $indexName;
       
   413         }
       
   414 
       
   415         $this->_indexes[$indexName] = $indexCandidate;
       
   416         return $this;
       
   417     }
       
   418 
       
   419     /**
       
   420      * @param ForeignKeyConstraint $constraint
       
   421      */
       
   422     protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
       
   423     {
       
   424         $constraint->setLocalTable($this);
       
   425         
       
   426         if(strlen($constraint->getName())) {
       
   427             $name = $constraint->getName();
       
   428         } else {
       
   429             $name = $this->_generateIdentifierName(
       
   430                 array_merge((array)$this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength()
       
   431             );
       
   432         }
       
   433         $name = strtolower($name);
       
   434 
       
   435         $this->_fkConstraints[$name] = $constraint;
       
   436         // add an explicit index on the foreign key columns. If there is already an index that fullfils this requirements drop the request.
       
   437         // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
       
   438         // lead to duplicates. This creates compuation overhead in this case, however no duplicate indexes are ever added (based on columns).
       
   439         $this->addIndex($constraint->getColumns());
       
   440     }
       
   441 
       
   442     /**
       
   443      * Does Table have a foreign key constraint with the given name?
       
   444      *      *
       
   445      * @param  string $constraintName
       
   446      * @return bool
       
   447      */
       
   448     public function hasForeignKey($constraintName)
       
   449     {
       
   450         $constraintName = strtolower($constraintName);
       
   451         return isset($this->_fkConstraints[$constraintName]);
       
   452     }
       
   453 
       
   454     /**
       
   455      * @param string $constraintName
       
   456      * @return ForeignKeyConstraint
       
   457      */
       
   458     public function getForeignKey($constraintName)
       
   459     {
       
   460         $constraintName = strtolower($constraintName);
       
   461         if(!$this->hasForeignKey($constraintName)) {
       
   462             throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
       
   463         }
       
   464 
       
   465         return $this->_fkConstraints[$constraintName];
       
   466     }
       
   467 
       
   468     /**
       
   469      * @return Column[]
       
   470      */
       
   471     public function getColumns()
       
   472     {
       
   473         $columns = $this->_columns;
       
   474 
       
   475         $pkCols = array();
       
   476         $fkCols = array();
       
   477 
       
   478         if ($this->hasIndex($this->_primaryKeyName)) {
       
   479             $pkCols = $this->getPrimaryKey()->getColumns();
       
   480         }
       
   481         foreach ($this->getForeignKeys() AS $fk) {
       
   482             /* @var $fk ForeignKeyConstraint */
       
   483             $fkCols = array_merge($fkCols, $fk->getColumns());
       
   484         }
       
   485         $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
       
   486 
       
   487         uksort($columns, function($a, $b) use($colNames) {
       
   488             return (array_search($a, $colNames) >= array_search($b, $colNames));
       
   489         });
       
   490         return $columns;
       
   491     }
       
   492 
       
   493 
       
   494     /**
       
   495      * Does this table have a column with the given name?
       
   496      *
       
   497      * @param  string $columnName
       
   498      * @return bool
       
   499      */
       
   500     public function hasColumn($columnName)
       
   501     {
       
   502         $columnName = $this->trimQuotes(strtolower($columnName));
       
   503         return isset($this->_columns[$columnName]);
       
   504     }
       
   505 
       
   506     /**
       
   507      * Get a column instance
       
   508      * 
       
   509      * @param  string $columnName
       
   510      * @return Column
       
   511      */
       
   512     public function getColumn($columnName)
       
   513     {
       
   514         $columnName = strtolower($this->trimQuotes($columnName));
       
   515         if (!$this->hasColumn($columnName)) {
       
   516             throw SchemaException::columnDoesNotExist($columnName, $this->_name);
       
   517         }
       
   518 
       
   519         return $this->_columns[$columnName];
       
   520     }
       
   521 
       
   522     /**
       
   523      * @return Index
       
   524      */
       
   525     public function getPrimaryKey()
       
   526     {
       
   527         return $this->getIndex($this->_primaryKeyName);
       
   528     }
       
   529 
       
   530     /**
       
   531      * Check if this table has a primary key.
       
   532      *
       
   533      * @return bool
       
   534      */
       
   535     public function hasPrimaryKey()
       
   536     {
       
   537         return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName));
       
   538     }
       
   539 
       
   540     /**
       
   541      * @param  string $indexName
       
   542      * @return bool
       
   543      */
       
   544     public function hasIndex($indexName)
       
   545     {
       
   546         $indexName = strtolower($indexName);
       
   547         return (isset($this->_indexes[$indexName]));
       
   548     }
       
   549 
       
   550     /**
       
   551      * @param  string $indexName
       
   552      * @return Index
       
   553      */
       
   554     public function getIndex($indexName)
       
   555     {
       
   556         $indexName = strtolower($indexName);
       
   557         if (!$this->hasIndex($indexName)) {
       
   558             throw SchemaException::indexDoesNotExist($indexName, $this->_name);
       
   559         }
       
   560         return $this->_indexes[$indexName];
       
   561     }
       
   562 
       
   563     /**
       
   564      * @return array
       
   565      */
       
   566     public function getIndexes()
       
   567     {
       
   568         return $this->_indexes;
       
   569     }
       
   570 
       
   571     /**
       
   572      * Get Constraints
       
   573      *
       
   574      * @return array
       
   575      */
       
   576     public function getForeignKeys()
       
   577     {
       
   578         return $this->_fkConstraints;
       
   579     }
       
   580 
       
   581     public function hasOption($name)
       
   582     {
       
   583         return isset($this->_options[$name]);
       
   584     }
       
   585 
       
   586     public function getOption($name)
       
   587     {
       
   588         return $this->_options[$name];
       
   589     }
       
   590 
       
   591     public function getOptions()
       
   592     {
       
   593         return $this->_options;
       
   594     }
       
   595 
       
   596     /**
       
   597      * @param Visitor $visitor
       
   598      */
       
   599     public function visit(Visitor $visitor)
       
   600     {
       
   601         $visitor->acceptTable($this);
       
   602 
       
   603         foreach ($this->getColumns() AS $column) {
       
   604             $visitor->acceptColumn($this, $column);
       
   605         }
       
   606 
       
   607         foreach ($this->getIndexes() AS $index) {
       
   608             $visitor->acceptIndex($this, $index);
       
   609         }
       
   610 
       
   611         foreach ($this->getForeignKeys() AS $constraint) {
       
   612             $visitor->acceptForeignKey($this, $constraint);
       
   613         }
       
   614     }
       
   615 
       
   616     /**
       
   617      * Clone of a Table triggers a deep clone of all affected assets
       
   618      */
       
   619     public function __clone()
       
   620     {
       
   621         foreach ($this->_columns AS $k => $column) {
       
   622             $this->_columns[$k] = clone $column;
       
   623         }
       
   624         foreach ($this->_indexes AS $k => $index) {
       
   625             $this->_indexes[$k] = clone $index;
       
   626         }
       
   627         foreach ($this->_fkConstraints AS $k => $fk) {
       
   628             $this->_fkConstraints[$k] = clone $fk;
       
   629             $this->_fkConstraints[$k]->setLocalTable($this);
       
   630         }
       
   631     }
       
   632 }