[FEATURE] Doctrine: Implement SchemaMigrationService
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / Schema / EventListener / SchemaColumnDefinitionListener.php
1 <?php
2 declare(strict_types=1);
3
4 namespace TYPO3\CMS\Core\Database\Schema;
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\Event\SchemaColumnDefinitionEventArgs;
20 use Doctrine\DBAL\Platforms\AbstractPlatform;
21 use Doctrine\DBAL\Schema\Column;
22 use Doctrine\DBAL\Types\Type;
23
24 /**
25 * Event listener to handle additional processing for custom
26 * doctrine types.
27 */
28 class SchemaColumnDefinitionListener
29 {
30 /**
31 * Listener for column definition events. This intercepts definitions
32 * for custom doctrine types and builds the appropriate Column Object.
33 *
34 * @param \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs $event
35 * @throws \Doctrine\DBAL\DBALException
36 */
37 public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $event)
38 {
39 $tableColumn = $event->getTableColumn();
40 $dbType = $this->getDatabaseType($tableColumn['Type']);
41 if ($dbType !== 'enum' && $dbType !== 'set') {
42 return;
43 }
44
45 $column = $this->getEnumerationTableColumnDefinition(
46 $tableColumn,
47 $event->getDatabasePlatform()
48 );
49
50 $event->setColumn($column);
51 $event->preventDefault();
52 }
53
54 /**
55 * Build a Doctrine column object for TYPE/TYPE columns.
56 *
57 * @param array $tableColumn
58 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
59 * @return \Doctrine\DBAL\Schema\Column
60 * @throws \Doctrine\DBAL\DBALException
61 * @todo: The $tableColumn source currently only support MySQL definition style.
62 */
63 protected function getEnumerationTableColumnDefinition(array $tableColumn, AbstractPlatform $platform): Column
64 {
65 $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
66
67 $options = [
68 'length' => $tableColumn['length'] ?: null,
69 'unsigned' => false,
70 'fixed' => false,
71 'default' => $tableColumn['default'] ?: null,
72 'notnull' => (bool)($tableColumn['null'] !== 'YES'),
73 'scale' => null,
74 'precision' => null,
75 'autoincrement' => false,
76 'comment' => $tableColumn['comment'] ?: null,
77 ];
78
79 $dbType = $this->getDatabaseType($tableColumn['type']);
80 $doctrineType = $platform->getDoctrineTypeMapping($dbType);
81
82 $column = new Column($tableColumn['field'], Type::getType($doctrineType), $options);
83 $column->setPlatformOption('unquotedValues', $this->getUnquotedEnumerationValues($tableColumn['type']));
84
85 return $column;
86 }
87
88 /**
89 * Extract the field type from the definition string
90 *
91 * @param string $typeDefiniton
92 * @return string
93 */
94 protected function getDatabaseType(string $typeDefiniton): string
95 {
96 $dbType = strtolower($typeDefiniton);
97 $dbType = strtok($dbType, '(), ');
98
99 return $dbType;
100 }
101
102 /**
103 * @param string $typeDefiniton
104 * @return array
105 */
106 protected function getUnquotedEnumerationValues(string $typeDefiniton): array
107 {
108 $valuesDefinition = preg_replace('#^(enum|set)\((.*)\)\s*$#i', '$2', $typeDefiniton);
109 $quoteChar = $valuesDefinition[0];
110 $separator = $quoteChar . ',' . $quoteChar;
111
112 $valuesDefinition = preg_replace(
113 '#' . $quoteChar . ',\s*' . $quoteChar . '#',
114 $separator,
115 $valuesDefinition
116 );
117
118 $values = explode($quoteChar . ',' . $quoteChar, substr($valuesDefinition, 1, -1));
119
120 return array_map(
121 function (string $value) use ($quoteChar) {
122 return str_replace($quoteChar . $quoteChar, $quoteChar, $value);
123 },
124 $values
125 );
126 }
127 }