source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/DBAL/Schema/Table.php @ 345

Last change on this file since 345 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 17.6 KB
Line 
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
22namespace Doctrine\DBAL\Schema;
23
24use Doctrine\DBAL\Types\Type;
25use Doctrine\DBAL\Schema\Visitor\Visitor;
26use 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 */
37class 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    public function removeForeignKey($constraintName)
469    {
470        $constraintName = strtolower($constraintName);
471        if(!$this->hasForeignKey($constraintName)) {
472            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
473        }
474
475        unset($this->_fkConstraints[$constraintName]);
476    }
477
478    /**
479     * @return Column[]
480     */
481    public function getColumns()
482    {
483        $columns = $this->_columns;
484
485        $pkCols = array();
486        $fkCols = array();
487
488        if ($this->hasPrimaryKey()) {
489            $pkCols = $this->getPrimaryKey()->getColumns();
490        }
491        foreach ($this->getForeignKeys() AS $fk) {
492            /* @var $fk ForeignKeyConstraint */
493            $fkCols = array_merge($fkCols, $fk->getColumns());
494        }
495        $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
496
497        uksort($columns, function($a, $b) use($colNames) {
498            return (array_search($a, $colNames) >= array_search($b, $colNames));
499        });
500        return $columns;
501    }
502
503
504    /**
505     * Does this table have a column with the given name?
506     *
507     * @param  string $columnName
508     * @return bool
509     */
510    public function hasColumn($columnName)
511    {
512        $columnName = $this->trimQuotes(strtolower($columnName));
513        return isset($this->_columns[$columnName]);
514    }
515
516    /**
517     * Get a column instance
518     *
519     * @param  string $columnName
520     * @return Column
521     */
522    public function getColumn($columnName)
523    {
524        $columnName = strtolower($this->trimQuotes($columnName));
525        if (!$this->hasColumn($columnName)) {
526            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
527        }
528
529        return $this->_columns[$columnName];
530    }
531
532    /**
533     * @return Index|null
534     */
535    public function getPrimaryKey()
536    {
537        if (!$this->hasPrimaryKey()) {
538            return null;
539        }
540        return $this->getIndex($this->_primaryKeyName);
541    }
542
543    /**
544     * Check if this table has a primary key.
545     *
546     * @return bool
547     */
548    public function hasPrimaryKey()
549    {
550        return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName));
551    }
552
553    /**
554     * @param  string $indexName
555     * @return bool
556     */
557    public function hasIndex($indexName)
558    {
559        $indexName = strtolower($indexName);
560        return (isset($this->_indexes[$indexName]));
561    }
562
563    /**
564     * @param  string $indexName
565     * @return Index
566     */
567    public function getIndex($indexName)
568    {
569        $indexName = strtolower($indexName);
570        if (!$this->hasIndex($indexName)) {
571            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
572        }
573        return $this->_indexes[$indexName];
574    }
575
576    /**
577     * @return array
578     */
579    public function getIndexes()
580    {
581        return $this->_indexes;
582    }
583
584    /**
585     * Get Constraints
586     *
587     * @return array
588     */
589    public function getForeignKeys()
590    {
591        return $this->_fkConstraints;
592    }
593
594    public function hasOption($name)
595    {
596        return isset($this->_options[$name]);
597    }
598
599    public function getOption($name)
600    {
601        return $this->_options[$name];
602    }
603
604    public function getOptions()
605    {
606        return $this->_options;
607    }
608
609    /**
610     * @param Visitor $visitor
611     */
612    public function visit(Visitor $visitor)
613    {
614        $visitor->acceptTable($this);
615
616        foreach ($this->getColumns() AS $column) {
617            $visitor->acceptColumn($this, $column);
618        }
619
620        foreach ($this->getIndexes() AS $index) {
621            $visitor->acceptIndex($this, $index);
622        }
623
624        foreach ($this->getForeignKeys() AS $constraint) {
625            $visitor->acceptForeignKey($this, $constraint);
626        }
627    }
628
629    /**
630     * Clone of a Table triggers a deep clone of all affected assets
631     */
632    public function __clone()
633    {
634        foreach ($this->_columns AS $k => $column) {
635            $this->_columns[$k] = clone $column;
636        }
637        foreach ($this->_indexes AS $k => $index) {
638            $this->_indexes[$k] = clone $index;
639        }
640        foreach ($this->_fkConstraints AS $k => $fk) {
641            $this->_fkConstraints[$k] = clone $fk;
642            $this->_fkConstraints[$k]->setLocalTable($this);
643        }
644    }
645}
Note: See TracBrowser for help on using the repository browser.