‪TYPO3CMS  ‪main
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\Types;
67 
73 {
77  protected ‪$table;
78 
82  protected ‪$platform;
83 
90  public function ‪__construct(AbstractPlatform ‪$platform = null)
91  {
92  // Register custom data types as no connection might have
93  // been established yet so the types would not be available
94  // when building tables/columns.
95  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
96  $connectionPool->registerDoctrineTypes();
97  $this->platform = ‪$platform ?: GeneralUtility::makeInstance(MySQLPlatform::class);
98  }
99 
107  public function ‪create(‪CreateTableStatement $tableStatement): Table
108  {
109  $this->table = GeneralUtility::makeInstance(
110  Table::class,
111  $tableStatement->tableName->getQuotedName(),
112  [],
113  [],
114  [],
115  [],
116  $this->buildTableOptions($tableStatement->tableOptions)
117  );
118 
119  foreach ($tableStatement->createDefinition->items as $item) {
120  switch (get_class($item)) {
121  case CreateColumnDefinitionItem::class:
122  $this->‪addColumn($item);
123  break;
124  case CreateIndexDefinitionItem::class:
125  $this->‪addIndex($item);
126  break;
127  case CreateForeignKeyDefinitionItem::class:
128  $this->‪addForeignKey($item);
129  break;
130  default:
131  throw new \RuntimeException(
132  'Unknown item definition of type "' . get_class($item) . '" encountered.',
133  1472044085
134  );
135  }
136  }
137 
138  return ‪$this->table;
139  }
140 
145  protected function ‪addColumn(‪CreateColumnDefinitionItem $item): Column
146  {
147  $column = $this->table->addColumn(
148  $item->columnName->getQuotedName(),
149  $this->getDoctrineColumnTypeName($item->dataType)
150  );
151 
152  $column->setNotnull($item->allowNull === false);
153  $column->setAutoincrement($item->autoIncrement);
154  $column->setComment((string)$item->comment);
155 
156  // Set default value (unless it's an auto increment column)
157  if ($item->hasDefaultValue && !$column->getAutoincrement()) {
158  $column->setDefault($item->defaultValue);
159  }
160 
161  if ($item->dataType->getLength()) {
162  $column->setLength($item->dataType->getLength());
163  }
164 
165  if ($item->dataType->getPrecision() >= 0) {
166  $column->setPrecision($item->dataType->getPrecision());
167  }
168 
169  if ($item->dataType->getScale() >= 0) {
170  $column->setScale($item->dataType->getScale());
171  }
172 
173  if ($item->dataType->isUnsigned()) {
174  $column->setUnsigned(true);
175  }
176 
177  // Select CHAR/VARCHAR or BINARY/VARBINARY
178  if ($item->dataType->isFixed()) {
179  $column->setFixed(true);
180  }
181 
182  if ($item->dataType instanceof ‪EnumDataType
183  || $item->dataType instanceof ‪SetDataType
184  ) {
185  $column->setPlatformOption('unquotedValues', $item->dataType->getValues());
186  }
187 
188  if ($item->index) {
189  $this->table->addIndex([$item->columnName->getQuotedName()]);
190  }
191 
192  if ($item->unique) {
193  $this->table->addUniqueIndex([$item->columnName->getQuotedName()]);
194  }
195 
196  if ($item->primary) {
197  $this->table->setPrimaryKey([$item->columnName->getQuotedName()]);
198  }
199 
200  if ($item->reference !== null) {
202  [$item->columnName->getQuotedName()],
203  $item->reference
204  );
205  }
206 
207  return $column;
208  }
209 
214  protected function ‪addIndex(‪CreateIndexDefinitionItem $item): Index
215  {
216  $indexName = $item->indexName->getQuotedName();
217 
218  $columnNames = array_map(
219  static function (‪IndexColumnName $columnName): string {
220  if ($columnName->length) {
221  return $columnName->columnName->getQuotedName() . '(' . $columnName->length . ')';
222  }
223  return $columnName->columnName->getQuotedName();
224  },
225  $item->columnNames
226  );
227 
228  if ($item->isPrimary) {
229  $this->table->setPrimaryKey($columnNames);
230  $index = $this->table->getPrimaryKey();
231  } else {
232  $index = GeneralUtility::makeInstance(
233  Index::class,
234  $indexName,
235  $columnNames,
236  $item->isUnique,
237  $item->isPrimary
238  );
239 
240  if ($item->isFulltext) {
241  $index->addFlag('fulltext');
242  } elseif ($item->isSpatial) {
243  $index->addFlag('spatial');
244  }
245 
246  $this->table = new Table(
247  $this->table->getQuotedName($this->platform),
248  $this->table->getColumns(),
249  array_merge($this->table->getIndexes(), [strtolower($indexName) => $index]),
250  [],
251  $this->table->getForeignKeys(),
252  $this->table->getOptions()
253  );
254  }
255 
256  return $index;
257  }
258 
262  protected function ‪addForeignKey(‪CreateForeignKeyDefinitionItem $item)
263  {
264  $indexName = $item->indexName->getQuotedName() ?: null;
265  $localColumnNames = array_map(
266  static function (‪IndexColumnName $columnName): string {
267  return $columnName->columnName->getQuotedName();
268  },
269  $item->columnNames
270  );
271  $this->‪addForeignKeyConstraint($localColumnNames, $item->reference, $indexName);
272  }
273 
279  protected function ‪addForeignKeyConstraint(
280  array $localColumnNames,
281  ‪ReferenceDefinition $referenceDefinition,
282  string $indexName = null
283  ) {
284  $foreignTableName = $referenceDefinition->tableName->getQuotedName();
285  $foreignColumnNames = array_map(
286  static function (‪IndexColumnName $columnName): string {
287  return $columnName->columnName->getQuotedName();
288  },
289  $referenceDefinition->columnNames
290  );
291 
292  $options = [
293  'onDelete' => $referenceDefinition->onDelete,
294  'onUpdate' => $referenceDefinition->onUpdate,
295  ];
296 
297  $this->table->addForeignKeyConstraint(
298  $foreignTableName,
299  $localColumnNames,
300  $foreignColumnNames,
301  $options,
302  $indexName
303  );
304  }
305 
309  protected function ‪getDoctrineColumnTypeName(‪AbstractDataType $dataType): string
310  {
311  switch (get_class($dataType)) {
312  case TinyIntDataType::class:
313  // TINYINT is MySQL specific and mapped to a standard SMALLINT
314  case SmallIntDataType::class:
315  $doctrineType = Types::SMALLINT;
316  break;
317  case MediumIntDataType::class:
318  // MEDIUMINT is MySQL specific and mapped to a standard INT
319  case IntegerDataType::class:
320  $doctrineType = Types::INTEGER;
321  break;
322  case BigIntDataType::class:
323  $doctrineType = Types::BIGINT;
324  break;
325  case BinaryDataType::class:
326  case VarBinaryDataType::class:
327  // CHAR/VARCHAR is determined by "fixed" column property
328  $doctrineType = Types::BINARY;
329  break;
330  case TinyBlobDataType::class:
331  case MediumBlobDataType::class:
332  case BlobDataType::class:
333  case LongBlobDataType::class:
334  // Actual field type is determined by field length
335  $doctrineType = Types::BLOB;
336  break;
337  case DateDataType::class:
338  $doctrineType = Types::DATE_MUTABLE;
339  break;
340  case TimestampDataType::class:
341  case DateTimeDataType::class:
342  // TIMESTAMP or DATETIME are determined by "version" column property
343  $doctrineType = Types::DATETIME_MUTABLE;
344  break;
345  case NumericDataType::class:
346  case DecimalDataType::class:
347  $doctrineType = Types::DECIMAL;
348  break;
349  case RealDataType::class:
350  case FloatDataType::class:
351  case DoubleDataType::class:
352  $doctrineType = Types::FLOAT;
353  break;
354  case TimeDataType::class:
355  $doctrineType = Types::TIME_MUTABLE;
356  break;
357  case TinyTextDataType::class:
358  case MediumTextDataType::class:
359  case TextDataType::class:
360  case LongTextDataType::class:
361  $doctrineType = Types::TEXT;
362  break;
363  case CharDataType::class:
364  case VarCharDataType::class:
365  $doctrineType = Types::STRING;
366  break;
367  case EnumDataType::class:
368  $doctrineType = ‪EnumType::TYPE;
369  break;
370  case SetDataType::class:
371  $doctrineType = ‪SetType::TYPE;
372  break;
373  case JsonDataType::class:
374  $doctrineType = Types::JSON;
375  break;
376  case YearDataType::class:
377  // The YEAR data type is MySQL specific and offers little to no benefit.
378  // The two-digit year logic implemented in this data type (1-69 mapped to
379  // 2001-2069, 70-99 mapped to 1970-1999) can be easily implemented in the
380  // application and for all other accounts it's an integer with a valid
381  // range of 1901 to 2155.
382  // Using a SMALLINT covers the value range and ensures database compatibility.
383  $doctrineType = Types::SMALLINT;
384  break;
385  default:
386  throw new \RuntimeException(
387  'Unsupported data type: ' . get_class($dataType) . '!',
388  1472046376
389  );
390  }
391 
392  return $doctrineType;
393  }
394 
398  protected function ‪buildTableOptions(array $tableOptions): array
399  {
400  $options = [];
401 
402  if (!empty($tableOptions['engine'])) {
403  $options['engine'] = (string)$tableOptions['engine'];
404  }
405  if (!empty($tableOptions['character_set'])) {
406  $options['charset'] = (string)$tableOptions['character_set'];
407  }
408  if (!empty($tableOptions['collation'])) {
409  $options['collate'] = (string)$tableOptions['collation'];
410  }
411  if (!empty($tableOptions['auto_increment'])) {
412  $options['auto_increment'] = (string)$tableOptions['auto_increment'];
413  }
414  if (!empty($tableOptions['comment'])) {
415  $options['comment'] = (string)$tableOptions['comment'];
416  }
417  if (!empty($tableOptions['row_format'])) {
418  $options['row_format'] = (string)$tableOptions['row_format'];
419  }
420 
421  return $options;
422  }
423 }
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BigIntDataType
Definition: BigIntDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TimeDataType
Definition: TimeDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\VarCharDataType
Definition: VarCharDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DateTimeDataType
Definition: DateTimeDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\JsonDataType
Definition: JsonDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateTableStatement
Definition: CreateTableStatement.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateColumnDefinitionItem
Definition: CreateColumnDefinitionItem.php:29
‪TYPO3\CMS\Core\Database\Schema\Types\SetType
Definition: SetType.php:29
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateIndexDefinitionItem
Definition: CreateIndexDefinitionItem.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\VarBinaryDataType
Definition: VarBinaryDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addIndex
‪addIndex(CreateIndexDefinitionItem $item)
Definition: TableBuilder.php:212
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\RealDataType
Definition: RealDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyTextDataType
Definition: TinyTextDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType
Definition: EnumType.php:29
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder
Definition: TableBuilder.php:73
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addForeignKey
‪addForeignKey(CreateForeignKeyDefinitionItem $item)
Definition: TableBuilder.php:260
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateForeignKeyDefinitionItem
Definition: CreateForeignKeyDefinitionItem.php:26
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType\TYPE
‪const TYPE
Definition: EnumType.php:30
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TextDataType
Definition: TextDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumTextDataType
Definition: MediumTextDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyBlobDataType
Definition: TinyBlobDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\create
‪create(CreateTableStatement $tableStatement)
Definition: TableBuilder.php:105
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\__construct
‪__construct(AbstractPlatform $platform=null)
Definition: TableBuilder.php:88
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\CharDataType
Definition: CharDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\SetDataType
Definition: SetDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\IntegerDataType
Definition: IntegerDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addForeignKeyConstraint
‪addForeignKeyConstraint(array $localColumnNames, ReferenceDefinition $referenceDefinition, string $indexName=null)
Definition: TableBuilder.php:277
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\AbstractDataType
Definition: AbstractDataType.php:27
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\addColumn
‪addColumn(CreateColumnDefinitionItem $item)
Definition: TableBuilder.php:143
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\LongTextDataType
Definition: LongTextDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumIntDataType
Definition: MediumIntDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\FloatDataType
Definition: FloatDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DoubleDataType
Definition: DoubleDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\MediumBlobDataType
Definition: MediumBlobDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TinyIntDataType
Definition: TinyIntDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BinaryDataType
Definition: BinaryDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DecimalDataType
Definition: DecimalDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\$platform
‪AbstractPlatform $platform
Definition: TableBuilder.php:80
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\IndexColumnName
Definition: IndexColumnName.php:28
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\BlobDataType
Definition: BlobDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\$table
‪Table $table
Definition: TableBuilder.php:76
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\buildTableOptions
‪buildTableOptions(array $tableOptions)
Definition: TableBuilder.php:396
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\LongBlobDataType
Definition: LongBlobDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\DateDataType
Definition: DateDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\NumericDataType
Definition: NumericDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\SmallIntDataType
Definition: SmallIntDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\EnumDataType
Definition: EnumDataType.php:26
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\ReferenceDefinition
Definition: ReferenceDefinition.php:28
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\YearDataType
Definition: YearDataType.php:25
‪TYPO3\CMS\Core\Database\Schema\Parser\AST\DataType\TimestampDataType
Definition: TimestampDataType.php:26
‪TYPO3\CMS\Core\Database\Schema\Parser\TableBuilder\getDoctrineColumnTypeName
‪getDoctrineColumnTypeName(AbstractDataType $dataType)
Definition: TableBuilder.php:307
‪TYPO3\CMS\Core\Database\Schema\Types\SetType\TYPE
‪const TYPE
Definition: SetType.php:30