TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
SchemaMigratorTest.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types=1);
3 
4 namespace TYPO3\CMS\Core\Tests\Functional\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\Schema\AbstractSchemaManager;
20 use Doctrine\DBAL\Schema\Table;
21 use Doctrine\DBAL\Types\BigIntType;
22 use Doctrine\DBAL\Types\IntegerType;
28 
33 {
37  protected $sqlReader;
38 
42  protected $connectionPool;
43 
47  protected $schemaManager;
48 
52  protected $subject;
53 
57  protected $tableName = 'a_test_table';
58 
64  protected function setUp()
65  {
66  parent::setUp();
67  $this->subject = GeneralUtility::makeInstance(SchemaMigrator::class);
68  $this->sqlReader = GeneralUtility::makeInstance(SqlReader::class);
69  $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
70  $this->schemaManager = $this->connectionPool->getConnectionForTable($this->tableName)->getSchemaManager();
71  $this->prepareTestTable();
72  }
73 
77  protected function tearDown()
78  {
79  parent::tearDown();
80 
81  if ($this->schemaManager->tablesExist([$this->tableName])) {
82  $this->schemaManager->dropTable($this->tableName);
83  }
84  if ($this->schemaManager->tablesExist(['zzz_deleted_' . $this->tableName])) {
85  $this->schemaManager->dropTable('zzz_deleted_' . $this->tableName);
86  }
87  if ($this->schemaManager->tablesExist(['another_test_table'])) {
88  $this->schemaManager->dropTable('another_test_table');
89  }
90  }
91 
95  public function createNewTable()
96  {
97  if ($this->schemaManager->tablesExist([$this->tableName])) {
98  $this->schemaManager->dropTable($this->tableName);
99  }
100 
101  $statements = $this->readFixtureFile('newTable');
102  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
103 
104  $this->subject->migrate(
105  $statements,
106  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
107  );
108 
109  $this->assertCount(5, $this->getTableDetails()->getColumns());
110  }
111 
115  public function createNewTableIfNotExists()
116  {
117  $statements = $this->readFixtureFile('ifNotExists');
118  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
119 
120  $this->subject->migrate(
121  $statements,
122  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
123  );
124 
125  $this->assertTrue($this->schemaManager->tablesExist(['another_test_table']));
126  }
127 
131  public function addNewColumns()
132  {
133  $statements = $this->readFixtureFile('addColumnsToTable');
134  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
135 
136  $this->subject->migrate(
137  $statements,
138  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
139  );
140 
141  $this->assertCount(7, $this->getTableDetails()->getColumns());
142  $this->assertTrue($this->getTableDetails()->hasColumn('title'));
143  $this->assertTrue($this->getTableDetails()->hasColumn('description'));
144  }
145 
149  public function changeExistingColumn()
150  {
151  $statements = $this->readFixtureFile('changeExistingColumn');
152  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
153 
154  $this->assertInstanceOf(IntegerType::class, $this->getTableDetails()->getColumn('uid')->getType());
155  $this->assertTrue($this->getTableDetails()->getColumn('uid')->getUnsigned());
156 
157  $this->subject->migrate(
158  $statements,
159  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
160  );
161 
162  $this->assertInstanceOf(BigIntType::class, $this->getTableDetails()->getColumn('uid')->getType());
163  $this->assertFalse($this->getTableDetails()->getColumn('uid')->getUnsigned());
164  }
165 
169  public function notNullWithoutDefaultValue()
170  {
171  $statements = $this->readFixtureFile('notNullWithoutDefaultValue');
172  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
173 
174  $this->subject->migrate(
175  $statements,
176  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
177  );
178 
179  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
180  $this->assertEmpty($updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
181  $this->assertTrue($this->getTableDetails()->getColumn('aTestField')->getNotnull());
182  }
183 
187  public function defaultNullWithoutNotNull()
188  {
189  $statements = $this->readFixtureFile('defaultNullWithoutNotNull');
190  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
191 
192  $this->subject->migrate(
193  $statements,
194  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
195  );
196 
197  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
198  $this->assertEmpty($updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
199  $this->assertFalse($this->getTableDetails()->getColumn('aTestField')->getNotnull());
200  $this->assertNull($this->getTableDetails()->getColumn('aTestField')->getDefault());
201  }
202 
206  public function renameUnusedField()
207  {
208  $statements = $this->readFixtureFile('unusedColumn');
209  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
210 
211  $this->subject->migrate(
212  $statements,
213  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
214  );
215 
216  $this->assertFalse($this->getTableDetails()->hasColumn('hidden'));
217  $this->assertTrue($this->getTableDetails()->hasColumn('zzz_deleted_hidden'));
218  }
219 
223  public function renameUnusedTable()
224  {
225  $statements = $this->readFixtureFile('unusedTable');
226  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
227 
228  $this->subject->migrate(
229  $statements,
230  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change_table']
231  );
232 
233  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
234  $this->assertContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
235  }
236 
240  public function dropUnusedField()
241  {
242  $connection = $this->connectionPool->getConnectionForTable($this->tableName);
243  $fromSchema = $this->schemaManager->createSchema();
244  $toSchema = clone $fromSchema;
245  $toSchema->getTable($this->tableName)->addColumn('zzz_deleted_testfield', 'integer');
246  $statements = $fromSchema->getMigrateToSql(
247  $toSchema,
248  $connection->getDatabasePlatform()
249  );
250  $connection->executeUpdate($statements[0]);
251  $this->assertTrue($this->getTableDetails()->hasColumn('zzz_deleted_testfield'));
252 
253  $statements = $this->readFixtureFile('newTable');
254  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
255  $this->subject->migrate(
256  $statements,
257  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['drop']
258  );
259 
260  $this->assertFalse($this->getTableDetails()->hasColumn('zzz_deleted_testfield'));
261  }
262 
266  public function dropUnusedTable()
267  {
268  $this->schemaManager->renameTable($this->tableName, 'zzz_deleted_' . $this->tableName);
269  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
270  $this->assertContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
271 
272  $statements = $this->readFixtureFile('newTable');
273  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
274  $this->subject->migrate(
275  $statements,
276  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['drop_table']
277  );
278 
279  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
280  $this->assertNotContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
281  }
282 
287  {
288  $statements = $this->readFixtureFile('addCreateChange');
289  $this->subject->install($statements, true);
290 
291  $this->assertContains('another_test_table', $this->schemaManager->listTableNames());
292  $this->assertTrue($this->getTableDetails()->hasColumn('title'));
293  $this->assertTrue($this->getTableDetails()->hasIndex('title'));
294  $this->assertTrue($this->getTableDetails()->getIndex('title')->isUnique());
295  $this->assertNotInstanceOf(BigIntType::class, $this->getTableDetails()->getColumn('pid')->getType());
296  }
297 
302  {
303  $statements = $this->readFixtureFile('addCreateChange');
304  $this->subject->install($statements);
305 
306  $this->assertContains('another_test_table', $this->schemaManager->listTableNames());
307  $this->assertTrue($this->getTableDetails()->hasColumn('title'));
308  $this->assertTrue($this->getTableDetails()->hasIndex('title'));
309  $this->assertTrue($this->getTableDetails()->getIndex('title')->isUnique());
310  $this->assertInstanceOf(BigIntType::class, $this->getTableDetails()->getColumn('pid')->getType());
311  }
312 
317  {
318  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', 'importStaticData.sql']));
319  $connection = $this->connectionPool->getConnectionForTable($this->tableName);
320  $statements = $this->sqlReader->getInsertStatementArray($sqlCode);
321  $this->subject->importStaticData($statements);
322 
323  $this->assertEquals(2, $connection->count('*', $this->tableName, []));
324  }
325 
330  {
331  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', 'importStaticData.sql']));
332  $statements = $this->sqlReader->getStatementArray($sqlCode);
333  $this->subject->importStaticData($statements);
334 
335  $this->assertNotContains('another_test_table', $this->schemaManager->listTableNames());
336  }
337 
341  public function changeTableEngine()
342  {
343  $statements = $this->readFixtureFile('alterTableEngine');
344  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
345 
346  $index = array_keys($updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change'])[0];
347  $this->assertStringEndsWith(
348  'ENGINE = MyISAM',
349  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change'][$index]
350  );
351 
352  $this->subject->migrate(
353  $statements,
354  $updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
355  );
356 
357  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
358  $this->assertEmpty($updateSuggestions[ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
359  }
360 
364  protected function prepareTestTable()
365  {
366  $statements = $this->readFixtureFile('newTable');
367  $this->subject->install($statements, true);
368  }
369 
375  protected function getTableDetails(): Table
376  {
377  return $this->schemaManager->listTableDetails($this->tableName);
378  }
379 
386  protected function readFixtureFile(string $fixtureName): array
387  {
388  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', $fixtureName]) . '.sql');
389 
390  return $this->sqlReader->getCreateTableStatementArray($sqlCode);
391  }
392 }
static makeInstance($className,...$constructorArguments)