‪TYPO3CMS  10.4
SchemaMigrator.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\DBALException;
21 use Doctrine\DBAL\Schema\Schema;
22 use Doctrine\DBAL\Schema\SchemaDiff;
23 use Doctrine\DBAL\Schema\Table;
28 
36 {
40  protected ‪$schema = [];
41 
56  public function ‪getUpdateSuggestions(array $statements, bool $remove = false): array
57  {
58  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
59  $tables = $this->‪parseCreateTableStatements($statements);
60 
61  $updateSuggestions = [];
62 
63  foreach ($connectionPool->getConnectionNames() as $connectionName) {
64  $connectionMigrator = ‪ConnectionMigrator::create(
65  $connectionName,
66  $tables
67  );
68 
69  $updateSuggestions[$connectionName] =
70  $connectionMigrator->getUpdateSuggestions($remove);
71  }
72 
73  return $updateSuggestions;
74  }
75 
89  public function ‪getSchemaDiffs(array $statements): array
90  {
91  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
92  $tables = $this->‪parseCreateTableStatements($statements);
93 
94  $schemaDiffs = [];
95 
96  foreach ($connectionPool->getConnectionNames() as $connectionName) {
97  $connectionMigrator = ‪ConnectionMigrator::create(
98  $connectionName,
99  $tables
100  );
101  $schemaDiffs[$connectionName] = $connectionMigrator->getSchemaDiff();
102  }
103 
104  return $schemaDiffs;
105  }
106 
121  public function ‪migrate(array $statements, array $selectedStatements): array
122  {
123  $result = [];
124  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
125  $updateSuggestionsPerConnection = array_merge_recursive(
126  $this->‪getUpdateSuggestions($statements),
127  $this->‪getUpdateSuggestions($statements, true)
128  );
129 
130  foreach ($updateSuggestionsPerConnection as $connectionName => $updateSuggestions) {
131  unset($updateSuggestions['tables_count'], $updateSuggestions['change_currentValue']);
132  $updateSuggestions = array_merge(...array_values($updateSuggestions));
133  $statementsToExecute = array_intersect_key($updateSuggestions, $selectedStatements);
134  if (count($statementsToExecute) === 0) {
135  continue;
136  }
137 
138  $connection = $connectionPool->getConnectionByName($connectionName);
139  foreach ($statementsToExecute as $hash => $statement) {
140  try {
141  $connection->executeUpdate($statement);
142  } catch (DBALException $e) {
143  $result[$hash] = $e->getPrevious()->getMessage();
144  }
145  }
146  }
147 
148  return $result;
149  }
150 
166  public function ‪install(array $statements, bool $createOnly = false): array
167  {
168  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
169  $tables = $this->‪parseCreateTableStatements($statements);
170  $result = [];
171 
172  foreach ($connectionPool->getConnectionNames() as $connectionName) {
173  $connectionMigrator = ‪ConnectionMigrator::create(
174  $connectionName,
175  $tables
176  );
177 
178  $lastResult = $connectionMigrator->install($createOnly);
179  $result = array_merge($result, $lastResult);
180  }
181 
182  return $result;
183  }
184 
192  public function ‪importStaticData(array $statements, bool $truncate = false): array
193  {
194  $result = [];
195  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
196  $insertStatements = [];
197 
198  foreach ($statements as $statement) {
199  // Only handle insert statements and extract the table at the same time. Extracting
200  // the table name is required to perform the inserts on the right connection.
201  if (preg_match('/^INSERT\s+INTO\s+`?(\w+)`?(.*)/i', $statement, $matches)) {
202  [, $tableName, $sqlFragment] = $matches;
203  $insertStatements[$tableName][] = sprintf(
204  'INSERT INTO %s %s',
205  $connectionPool->getConnectionForTable($tableName)->quoteIdentifier($tableName),
206  rtrim($sqlFragment, ';')
207  );
208  }
209  }
210 
211  foreach ($insertStatements as $tableName => $perTableStatements) {
212  $connection = $connectionPool->getConnectionForTable($tableName);
213 
214  if ($truncate) {
215  $connection->truncate($tableName);
216  }
217 
218  foreach ((array)$perTableStatements as $statement) {
219  try {
220  $connection->executeUpdate($statement);
221  $result[$statement] = '';
222  } catch (DBALException $e) {
223  $result[$statement] = $e->getPrevious()->getMessage();
224  }
225  }
226  }
227 
228  return $result;
229  }
230 
242  public function ‪parseCreateTableStatements(array $statements): array
243  {
244  $tables = [];
245  foreach ($statements as $statement) {
246  $createTableParser = GeneralUtility::makeInstance(Parser::class, $statement);
247 
248  // We need to keep multiple table definitions at this point so
249  // that Extensions can modify existing tables.
250  try {
251  $tables[] = $createTableParser->parse();
252  } catch (‪StatementException $statementException) {
253  // Enrich the error message with the full invalid statement
254  throw new ‪StatementException(
255  $statementException->getMessage() . ' in statement: ' . LF . $statement,
256  1476171315,
257  $statementException
258  );
259  }
260  }
261 
262  // Flatten the array of arrays by one level
263  $tables = array_merge(...$tables);
264 
265  // Add default TCA fields
266  $defaultTcaSchema = GeneralUtility::makeInstance(DefaultTcaSchema::class);
267  $tables = $defaultTcaSchema->enrich($tables);
268  // Ensure the default TCA fields are ordered
269  foreach ($tables as $k => $table) {
270  // @deprecated since v10 and will be removed in TYPO3 v11. "pages_language_overlay" is not to be added anymore in TYPO3 v10.
271  if ($table->getName() === 'pages_language_overlay') {
272  unset($tables[$k]);
273  continue;
274  }
275  $prioritizedColumnNames = $defaultTcaSchema->getPrioritizedFieldNames($table->getName());
276  // no TCA table
277  if (empty($prioritizedColumnNames)) {
278  continue;
279  }
280 
281  $prioritizedColumns = [];
282  $nonPrioritizedColumns = [];
283 
284  foreach ($table->getColumns() as $columnObject) {
285  if (in_array($columnObject->getName(), $prioritizedColumnNames, true)) {
286  $prioritizedColumns[] = $columnObject;
287  } else {
288  $nonPrioritizedColumns[] = $columnObject;
289  }
290  }
291 
292  $tables[$k] = new Table(
293  $table->getName(),
294  array_merge($prioritizedColumns, $nonPrioritizedColumns),
295  $table->getIndexes(),
296  $table->getForeignKeys(),
297  0,
298  $table->getOptions()
299  );
300  }
301 
302  return $tables;
303  }
304 }
‪TYPO3\CMS\Core\Database\Schema\ConnectionMigrator\create
‪static ConnectionMigrator create(string $connectionName, array $tables)
Definition: ConnectionMigrator.php:78
‪TYPO3\CMS\Core\Database\Schema\Exception\StatementException
Definition: StatementException.php:24
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\importStaticData
‪array importStaticData(array $statements, bool $truncate=false)
Definition: SchemaMigrator.php:191
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\$schema
‪Schema[] $schema
Definition: SchemaMigrator.php:39
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\getSchemaDiffs
‪SchemaDiff[] getSchemaDiffs(array $statements)
Definition: SchemaMigrator.php:88
‪TYPO3\CMS\Core\Database\Schema
Definition: Comparator.php:18
‪TYPO3\CMS\Core\Database\Schema\Parser\Parser
Definition: Parser.php:71
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator
Definition: SchemaMigrator.php:36
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\getUpdateSuggestions
‪array[] getUpdateSuggestions(array $statements, bool $remove=false)
Definition: SchemaMigrator.php:55
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\install
‪array[] install(array $statements, bool $createOnly=false)
Definition: SchemaMigrator.php:165
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\parseCreateTableStatements
‪Table[] parseCreateTableStatements(array $statements)
Definition: SchemaMigrator.php:241
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator\migrate
‪array migrate(array $statements, array $selectedStatements)
Definition: SchemaMigrator.php:120