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