‪TYPO3CMS  11.5
TableBuilder.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Platforms\AbstractPlatform;
21 use Doctrine\DBAL\Platforms\MySqlPlatform;
22 use Doctrine\DBAL\Schema\Column;
23 use Doctrine\DBAL\Schema\Index;
24 use Doctrine\DBAL\Schema\Table;
25 use Doctrine\DBAL\Types\Type;
26 use Doctrine\DBAL\Types\Types;
68 
74 {
78  protected ‪$table;
79 
83  protected ‪$platform;
84 
92  public function ‪__construct(AbstractPlatform ‪$platform = null)
93  {
94  // Register custom data types as no connection might have
95  // been established yet so the types would not be available
96  // when building tables/columns.
97  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
98 
99  foreach ($connectionPool->getCustomDoctrineTypes() as $type => $className) {
100  if (!Type::hasType($type)) {
101  Type::addType($type, $className);
102  }
103  }
104  $this->platform = ‪$platform ?: GeneralUtility::makeInstance(MySqlPlatform::class);
105  }
106 
116  public function ‪create(‪CreateTableStatement $tableStatement): Table
117  {
118  $this->table = GeneralUtility::makeInstance(
119  Table::class,
120  $tableStatement->tableName->getQuotedName(),
121  [],
122  [],
123  [],
124  0,
125  $this->buildTableOptions($tableStatement->tableOptions)
126  );
127 
128  foreach ($tableStatement->createDefinition->items as $item) {
129  switch (get_class($item)) {
130  case CreateColumnDefinitionItem::class:
131  $this->‪addColumn($item);
132  break;
133  case CreateIndexDefinitionItem::class:
134  $this->‪addIndex($item);
135  break;
136  case CreateForeignKeyDefinitionItem::class:
137  $this->‪addForeignKey($item);
138  break;
139  default:
140  throw new \RuntimeException(
141  'Unknown item definition of type "' . get_class($item) . '" encountered.',
142  1472044085
143  );
144  }
145  }
146 
147  return ‪$this->table;
148  }
149 
156  protected function ‪addColumn(‪CreateColumnDefinitionItem $item): Column
157  {
158  $column = $this->table->addColumn(
159  $item->columnName->getQuotedName(),
160  $this->getDoctrineColumnTypeName($item->dataType)
161  );
162 
163  $column->setNotnull(!$item->allowNull);
164  $column->setAutoincrement((bool)$item->autoIncrement);
165  $column->setComment($item->comment);
166 
167  // Set default value (unless it's an auto increment column)
168  if ($item->hasDefaultValue && !$column->getAutoincrement()) {
169  $column->setDefault($item->defaultValue);
170  }
171 
172  if ($item->dataType->getLength()) {
173  $column->setLength($item->dataType->getLength());
174  }
175 
176  if ($item->dataType->getPrecision() >= 0) {
177  $column->setPrecision($item->dataType->getPrecision());
178  }
179 
180  if ($item->dataType->getScale() >= 0) {
181  $column->setScale($item->dataType->getScale());
182  }
183 
184  if ($item->dataType->isUnsigned()) {
185  $column->setUnsigned(true);
186  }
187 
188  // Select CHAR/VARCHAR or BINARY/VARBINARY
189  if ($item->dataType->isFixed()) {
190  $column->setFixed(true);
191  }
192 
193  if ($item->dataType instanceof ‪EnumDataType
194  || $item->dataType instanceof ‪SetDataType
195  ) {
196  $column->setPlatformOption('unquotedValues', $item->dataType->getValues());
197  }
198 
199  if ($item->index) {
200  $this->table->addIndex([$item->columnName->getQuotedName()]);
201  }
202 
203  if ($item->unique) {
204  $this->table->addUniqueIndex([$item->columnName->getQuotedName()]);
205  }
206 
207  if ($item->primary) {
208  $this->table->setPrimaryKey([$item->columnName->getQuotedName()]);
209  }
210 
211  if ($item->reference !== null) {
213  [$item->columnName->getQuotedName()],
214  $item->reference
215  );
216  }
217 
218  return $column;
219  }
220 
227  protected function ‪addIndex(‪CreateIndexDefinitionItem $item): Index
228  {
229  $indexName = $item->indexName->getQuotedName();
230 
231  $columnNames = array_map(
232  static function (‪IndexColumnName $columnName) {
233  if ($columnName->length) {
234  return $columnName->columnName->getQuotedName() . '(' . $columnName->length . ')';
235  }
236  return $columnName->columnName->getQuotedName();
237  },
238  $item->columnNames
239  );
240 
241  if ($item->isPrimary) {
242  $this->table->setPrimaryKey($columnNames);
243  $index = $this->table->getPrimaryKey();
244  } else {
245  $index = GeneralUtility::makeInstance(
246  Index::class,
247  $indexName,
248  $columnNames,
249  $item->isUnique,
250  $item->isPrimary
251  );
252 
253  if ($item->isFulltext) {
254  $index->addFlag('fulltext');
255  } elseif ($item->isSpatial) {
256  $index->addFlag('spatial');
257  }
258 
259  $this->table = new Table(
260  $this->table->getQuotedName($this->platform),
261  $this->table->getColumns(),
262  array_merge($this->table->getIndexes(), [strtolower($indexName) => $index]),
263  $this->table->getForeignKeys(),
264  0,
265  $this->table->getOptions()
266  );
267  }
268 
269  return $index;
270  }
271 
277  protected function ‪addForeignKey(‪CreateForeignKeyDefinitionItem $item)
278  {
279  $indexName = $item->indexName->getQuotedName() ?: null;
280  $localColumnNames = array_map(
281  static function (‪IndexColumnName $columnName) {
282  return $columnName->columnName->getQuotedName();
283  },
284  $item->columnNames
285  );
286  $this->‪addForeignKeyConstraint($localColumnNames, $item->reference, $indexName);
287  }
288 
296  protected function ‪addForeignKeyConstraint(
297  array $localColumnNames,
298  ‪ReferenceDefinition $referenceDefinition,
299  string $indexName = null
300  ) {
301  $foreignTableName = $referenceDefinition->tableName->getQuotedName();
302  $foreignColumnNames = array_map(
303  static function (‪IndexColumnName $columnName) {
304  return $columnName->columnName->getQuotedName();
305  },
306  $referenceDefinition->columnNames
307  );
308 
309  $options = [
310  'onDelete' => $referenceDefinition->onDelete,
311  'onUpdate' => $referenceDefinition->onUpdate,
312  ];
313 
314  $this->table->addForeignKeyConstraint(
315  $foreignTableName,
316  $localColumnNames,
317  $foreignColumnNames,
318  $options,
319  $indexName
320  );
321  }
322 
328  protected function ‪getDoctrineColumnTypeName(‪AbstractDataType $dataType): string
329  {
330  switch (get_class($dataType)) {
331  case TinyIntDataType::class:
332  // TINYINT is MySQL specific and mapped to a standard SMALLINT
333  case SmallIntDataType::class:
334  $doctrineType = Types::SMALLINT;
335  break;
336  case MediumIntDataType::class:
337  // MEDIUMINT is MySQL specific and mapped to a standard INT
338  case IntegerDataType::class:
339  $doctrineType = Types::INTEGER;
340  break;
341  case BigIntDataType::class:
342  $doctrineType = Types::BIGINT;
343  break;
344  case BinaryDataType::class:
345  case VarBinaryDataType::class:
346  // CHAR/VARCHAR is determined by "fixed" column property
347  $doctrineType = Types::BINARY;
348  break;
349  case TinyBlobDataType::class:
350  case MediumBlobDataType::class:
351  case BlobDataType::class:
352  case LongBlobDataType::class:
353  // Actual field type is determined by field length
354  $doctrineType = Types::BLOB;
355  break;
356  case DateDataType::class:
357  $doctrineType = Types::DATE_MUTABLE;
358  break;
359  case TimestampDataType::class:
360  case DateTimeDataType::class:
361  // TIMESTAMP or DATETIME are determined by "version" column property
362  $doctrineType = Types::DATETIME_MUTABLE;
363  break;
364  case NumericDataType::class:
365  case DecimalDataType::class:
366  $doctrineType = Types::DECIMAL;
367  break;
368  case RealDataType::class:
369  case FloatDataType::class:
370  case DoubleDataType::class:
371  $doctrineType = Types::FLOAT;
372  break;
373  case TimeDataType::class:
374  $doctrineType = Types::TIME_MUTABLE;
375  break;
376  case TinyTextDataType::class:
377  case MediumTextDataType::class:
378  case TextDataType::class:
379  case LongTextDataType::class:
380  $doctrineType = Types::TEXT;
381  break;
382  case CharDataType::class:
383  case VarCharDataType::class:
384  $doctrineType = Types::STRING;
385  break;
386  case EnumDataType::class:
387  $doctrineType = ‪EnumType::TYPE;
388  break;
389  case SetDataType::class:
390  $doctrineType = ‪SetType::TYPE;
391  break;
392  case JsonDataType::class:
393  // JSON is not supported in Doctrine 2.5, mapping to the more generic TEXT type
394  $doctrineType = Types::TEXT;
395  break;
396  case YearDataType::class:
397  // The YEAR data type is MySQL specific and offers little to no benefit.
398  // The two-digit year logic implemented in this data type (1-69 mapped to
399  // 2001-2069, 70-99 mapped to 1970-1999) can be easily implemented in the
400  // application and for all other accounts it's an integer with a valid
401  // range of 1901 to 2155.
402  // Using a SMALLINT covers the value range and ensures database compatibility.
403  $doctrineType = Types::SMALLINT;
404  break;
405  default:
406  throw new \RuntimeException(
407  'Unsupported data type: ' . get_class($dataType) . '!',
408  1472046376
409  );
410  }
411 
412  return $doctrineType;
413  }
414 
421  protected function ‪buildTableOptions(array $tableOptions): array
422  {
423  $options = [];
424 
425  if (!empty($tableOptions['engine'])) {
426  $options['engine'] = (string)$tableOptions['engine'];
427  }
428  if (!empty($tableOptions['character_set'])) {
429  $options['charset'] = (string)$tableOptions['character_set'];
430  }
431  if (!empty($tableOptions['collation'])) {
432  $options['collate'] = (string)$tableOptions['collation'];
433  }
434  if (!empty($tableOptions['auto_increment'])) {
435  $options['auto_increment'] = (string)$tableOptions['auto_increment'];
436  }
437  if (!empty($tableOptions['comment'])) {
438  $options['comment'] = (string)$tableOptions['comment'];
439  }
440  if (!empty($tableOptions['row_format'])) {
441  $options['row_format'] = (string)$tableOptions['row_format'];
442  }
443 
444  return $options;
445  }
446 }
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BigIntDataType
Definition: BigIntDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TimeDataType
Definition: TimeDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\VarCharDataType
Definition: VarCharDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DateTimeDataType
Definition: DateTimeDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\JsonDataType
Definition: JsonDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateTableStatement
Definition: CreateTableStatement.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateColumnDefinitionItem
Definition: CreateColumnDefinitionItem.php:27
‪TYPO3\CMS\Core\Database\Schema\Types\SetType
Definition: SetType.php:27
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateIndexDefinitionItem
Definition: CreateIndexDefinitionItem.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addIndex
‪Doctrine DBAL Schema Index addIndex(CreateIndexDefinitionItem $item)
Definition: TableBuilder.php:225
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\VarBinaryDataType
Definition: VarBinaryDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\RealDataType
Definition: RealDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyTextDataType
Definition: TinyTextDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType
Definition: EnumType.php:27
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder
Definition: TableBuilder.php:74
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addForeignKey
‪addForeignKey(CreateForeignKeyDefinitionItem $item)
Definition: TableBuilder.php:275
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateForeignKeyDefinitionItem
Definition: CreateForeignKeyDefinitionItem.php:24
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType\TYPE
‪const TYPE
Definition: EnumType.php:28
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TextDataType
Definition: TextDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumTextDataType
Definition: MediumTextDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\create
‪Doctrine DBAL Schema Table create(CreateTableStatement $tableStatement)
Definition: TableBuilder.php:114
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyBlobDataType
Definition: TinyBlobDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\getDoctrineColumnTypeName
‪string getDoctrineColumnTypeName(AbstractDataType $dataType)
Definition: TableBuilder.php:326
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\__construct
‪__construct(AbstractPlatform $platform=null)
Definition: TableBuilder.php:90
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\CharDataType
Definition: CharDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\SetDataType
Definition: SetDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\IntegerDataType
Definition: IntegerDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\buildTableOptions
‪array buildTableOptions(array $tableOptions)
Definition: TableBuilder.php:419
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addForeignKeyConstraint
‪addForeignKeyConstraint(array $localColumnNames, ReferenceDefinition $referenceDefinition, string $indexName=null)
Definition: TableBuilder.php:294
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\AbstractDataType
Definition: AbstractDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\LongTextDataType
Definition: LongTextDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumIntDataType
Definition: MediumIntDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\FloatDataType
Definition: FloatDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DoubleDataType
Definition: DoubleDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumBlobDataType
Definition: MediumBlobDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyIntDataType
Definition: TinyIntDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BinaryDataType
Definition: BinaryDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DecimalDataType
Definition: DecimalDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\$platform
‪AbstractPlatform $platform
Definition: TableBuilder.php:81
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\IndexColumnName
Definition: IndexColumnName.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BlobDataType
Definition: BlobDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\$table
‪Table $table
Definition: TableBuilder.php:77
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\LongBlobDataType
Definition: LongBlobDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DateDataType
Definition: DateDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\NumericDataType
Definition: NumericDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\SmallIntDataType
Definition: SmallIntDataType.php:23
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addColumn
‪Doctrine DBAL Schema Column addColumn(CreateColumnDefinitionItem $item)
Definition: TableBuilder.php:154
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\EnumDataType
Definition: EnumDataType.php:24
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\ReferenceDefinition
Definition: ReferenceDefinition.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\YearDataType
Definition: YearDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TimestampDataType
Definition: TimestampDataType.php:24
‪TYPO3\CMS\Core\Database\Schema\Types\SetType\TYPE
‪const TYPE
Definition: SetType.php:28