Skip to content

Commit 4a97775

Browse files
committed
added a schema lock file to tdbm
1 parent 63c78c0 commit 4a97775

9 files changed

+122
-41
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ vendor/*
1616
.phpunit.result.cache
1717
/phpbench.sh
1818
/xdebug/
19+
.php_cs.cache
20+
tdbm.lock.yml

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"ext-PDO": "*",
4040
"ext-json": "*",
4141
"ext-hash": "*",
42-
"ext-filter": "*"
42+
"ext-filter": "*",
43+
"brain-diminished/schema-version-control": "^1.0"
4344
},
4445
"require-dev" : {
4546
"phpunit/phpunit": "^7.4.4 || ^8.0.0",

src/TDBMSchemaAnalyzer.php

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace TheCodingMachine\TDBM;
55

6+
use BrainDiminished\SchemaVersionControl\SchemaVersionControlService;
67
use Doctrine\Common\Cache\Cache;
78
use Doctrine\DBAL\Connection;
89
use Doctrine\DBAL\Schema\Column;
@@ -12,12 +13,16 @@
1213
use Doctrine\DBAL\Types\DateType;
1314
use Doctrine\DBAL\Types\Type;
1415
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
16+
use TheCodingMachine\TDBM\Utils\ImmutableCaster;
17+
use TheCodingMachine\TDBM\Utils\RootProjectLocator;
1518

1619
/**
1720
* This class is used to analyze the schema and return valuable information / hints.
1821
*/
1922
class TDBMSchemaAnalyzer
2023
{
24+
const schemaFileName = 'tdbm.lock.yml';
25+
2126
private $connection;
2227

2328
/**
@@ -40,6 +45,11 @@ class TDBMSchemaAnalyzer
4045
*/
4146
private $schemaAnalyzer;
4247

48+
/**
49+
* @var SchemaVersionControlService
50+
*/
51+
private $schemaVersionControlService;
52+
4353
/**
4454
* @param Connection $connection The DBAL DB connection to use
4555
* @param Cache $cache A cache service to be used
@@ -51,6 +61,7 @@ public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer
5161
$this->connection = $connection;
5262
$this->cache = $cache;
5363
$this->schemaAnalyzer = $schemaAnalyzer;
64+
$this->schemaVersionControlService = new SchemaVersionControlService($this->connection, self::getLockFilePath());
5465
}
5566

5667
/**
@@ -67,53 +78,35 @@ public function getCachePrefix(): string
6778
return $this->cachePrefix;
6879
}
6980

81+
public static function getLockFilePath(): string
82+
{
83+
return RootProjectLocator::getRootLocationPath().self::schemaFileName;
84+
}
85+
7086
/**
7187
* Returns the (cached) schema.
72-
*
73-
* @return Schema
7488
*/
75-
public function getSchema(): Schema
89+
public function getSchema(bool $ignoreCache = false): Schema
7690
{
7791
if ($this->schema === null) {
7892
$cacheKey = $this->getCachePrefix().'_immutable_schema';
79-
if ($this->cache->contains($cacheKey)) {
93+
if (!$ignoreCache && $this->cache->contains($cacheKey)) {
8094
$this->schema = $this->cache->fetch($cacheKey);
95+
} elseif (!file_exists(self::getLockFilePath())) {
96+
throw new TDBMException('No tdbm lock file found. Please regenerate DAOs and Beans.');
8197
} else {
82-
$this->schema = $this->connection->getSchemaManager()->createSchema();
83-
$this->castSchemaToImmutable($this->schema);
98+
$this->schema = $this->schemaVersionControlService->loadSchemaFile();
99+
ImmutableCaster::castSchemaToImmutable($this->schema);
84100
$this->cache->save($cacheKey, $this->schema);
85101
}
86102
}
87103

88104
return $this->schema;
89105
}
90106

91-
private function castSchemaToImmutable(Schema $schema): void
107+
public function generateLockFile(): void
92108
{
93-
foreach ($schema->getTables() as $table) {
94-
foreach ($table->getColumns() as $column) {
95-
$this->toImmutableType($column);
96-
}
97-
}
98-
}
99-
100-
/**
101-
* Changes the type of a column to an immutable date type if the type is a date.
102-
* This is needed because by default, when reading a Schema, Doctrine assumes a mutable datetime.
103-
*/
104-
private function toImmutableType(Column $column): void
105-
{
106-
$mapping = [
107-
Type::DATE => Type::DATE_IMMUTABLE,
108-
Type::DATETIME => Type::DATETIME_IMMUTABLE,
109-
Type::DATETIMETZ => Type::DATETIMETZ_IMMUTABLE,
110-
Type::TIME => Type::TIME_IMMUTABLE
111-
];
112-
113-
$typeName = $column->getType()->getName();
114-
if (isset($mapping[$typeName])) {
115-
$column->setType(Type::getType($mapping[$typeName]));
116-
}
109+
$this->schemaVersionControlService->dumpSchema();
117110
}
118111

119112
/**

src/Utils/ImmutableCaster.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace TheCodingMachine\TDBM\Utils;
4+
5+
use Doctrine\DBAL\Schema\Column;
6+
use Doctrine\DBAL\Schema\Schema;
7+
use Doctrine\DBAL\Types\Type;
8+
9+
class ImmutableCaster
10+
{
11+
public static function castSchemaToImmutable(Schema $schema): void
12+
{
13+
foreach ($schema->getTables() as $table) {
14+
foreach ($table->getColumns() as $column) {
15+
self::toImmutableType($column);
16+
}
17+
}
18+
}
19+
20+
/**
21+
* Changes the type of a column to an immutable date type if the type is a date.
22+
* This is needed because by default, when reading a Schema, Doctrine assumes a mutable datetime.
23+
*/
24+
private static function toImmutableType(Column $column): void
25+
{
26+
$mapping = [
27+
Type::DATE => Type::DATE_IMMUTABLE,
28+
Type::DATETIME => Type::DATETIME_IMMUTABLE,
29+
Type::DATETIMETZ => Type::DATETIMETZ_IMMUTABLE,
30+
Type::TIME => Type::TIME_IMMUTABLE
31+
];
32+
33+
$typeName = $column->getType()->getName();
34+
if (isset($mapping[$typeName])) {
35+
$column->setType(Type::getType($mapping[$typeName]));
36+
}
37+
}
38+
}

src/Utils/RootProjectLocator.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\TDBM\Utils;
5+
6+
class RootProjectLocator
7+
{
8+
public static function getRootLocationPath(): string
9+
{
10+
$reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class);
11+
return dirname($reflection->getFileName(), 3).'/';
12+
}
13+
}

src/Utils/TDBMDaoGenerator.php

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@
3333
*/
3434
class TDBMDaoGenerator
3535
{
36-
/**
37-
* @var Schema
38-
*/
39-
private $schema;
40-
4136
/**
4237
* @var TDBMSchemaAnalyzer
4338
*/
@@ -66,7 +61,6 @@ class TDBMDaoGenerator
6661
public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
6762
{
6863
$this->configuration = $configuration;
69-
$this->schema = $tdbmSchemaAnalyzer->getSchema();
7064
$this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
7165
$this->namingStrategy = $configuration->getNamingStrategy();
7266
$this->eventDispatcher = $configuration->getGeneratorEventDispatcher();
@@ -79,9 +73,11 @@ public function __construct(ConfigurationInterface $configuration, TDBMSchemaAna
7973
*/
8074
public function generateAllDaosAndBeans(): void
8175
{
82-
// TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
76+
$this->tdbmSchemaAnalyzer->generateLockFile();
77+
$schema = $this->tdbmSchemaAnalyzer->getSchema();
8378

84-
$tableList = $this->schema->getTables();
79+
// TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
80+
$tableList = $schema->getTables();
8581

8682
// Remove all beans and daos from junction tables
8783
$junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true);
@@ -97,7 +93,7 @@ public function generateAllDaosAndBeans(): void
9793

9894
$beanDescriptors = [];
9995

100-
$beanRegistry = new BeanRegistry($this->configuration, $this->schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy);
96+
$beanRegistry = new BeanRegistry($this->configuration, $schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy);
10197
foreach ($tableList as $table) {
10298
$beanDescriptors[] = $beanRegistry->addBeanForTable($table);
10399
}

tests/Commands/GenerateCommandTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Symfony\Component\Console\Input\InputInterface;
1111
use Symfony\Component\Console\Output\BufferedOutput;
1212
use Symfony\Component\Console\Output\OutputInterface;
13+
use TheCodingMachine\TDBM\TDBMSchemaAnalyzer;
1314

1415
class GenerateCommandTest extends TDBMAbstractServiceTest
1516
{
@@ -24,9 +25,16 @@ public function testCall(): void
2425
$input = new ArrayInput([
2526
], self::getInputDefinition());
2627

28+
//let's delete the lock file
29+
$schemaFilePath = TDBMSchemaAnalyzer::getLockFilePath();
30+
if (file_exists($schemaFilePath)) {
31+
unlink($schemaFilePath);
32+
}
2733
$result = $this->callCommand(new GenerateCommand($this->getConfiguration()), $input);
2834

2935
$this->assertContains('Finished regenerating DAOs and beans', $result);
36+
//Check that the lock file was generated
37+
$this->assertFileExists($schemaFilePath);
3038
}
3139

3240
/**

tests/TDBMDaoGeneratorTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,19 @@ public function testDaoGeneration(): void
114114
touch($dummyFile);
115115
$this->assertFileExists($dummyFile);
116116

117+
//let's delete the lock file
118+
$schemaFilePath = TDBMSchemaAnalyzer::getLockFilePath();
119+
if (file_exists($schemaFilePath)) {
120+
unlink($schemaFilePath);
121+
}
122+
117123
$this->tdbmDaoGenerator->generateAllDaosAndBeans();
118124

119125
$this->assertFileNotExists($dummyFile);
120126

127+
//Check that the lock file was generated
128+
$this->assertFileExists($schemaFilePath);
129+
121130
// Let's require all files to check they are valid PHP!
122131
// Test the daoFactory
123132
require_once $this->rootPath . 'src/Test/Dao/Generated/DaoFactory.php';

tests/TDBMSchemaAnalyzerTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
use Doctrine\Common\Cache\ArrayCache;
2525
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
26+
use TheCodingMachine\TDBM\Utils\ImmutableCaster;
2627

2728
class TDBMSchemaAnalyzerTest extends TDBMAbstractServiceTest
2829
{
@@ -38,6 +39,26 @@ protected function setUp(): void
3839
$this->tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer(self::getConnection(), new ArrayCache(), $schemaAnalyzer);
3940
}
4041

42+
public function testSchemaLock(): void
43+
{
44+
$schemaFromConnec = self::getConnection()->getSchemaManager()->createSchema();
45+
$tableNames = [];
46+
//lock file doesn't save the database name so we have to replace it manually.
47+
foreach ($schemaFromConnec->getTableNames() as $tableName) {
48+
$tableNames[] = str_replace('tdbm_testcase', 'public', $tableName);
49+
}
50+
ImmutableCaster::castSchemaToImmutable($schemaFromConnec);
51+
52+
$schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), new ArrayCache(), 'prefix_');
53+
$cache = new ArrayCache();
54+
$tdbmSchemaAnalyzer1 = new TDBMSchemaAnalyzer(self::getConnection(), $cache, $schemaAnalyzer);
55+
56+
$schemaFromAnalyser = $tdbmSchemaAnalyzer1->getSchema(true);
57+
$schemaFromAnalyserCached = $tdbmSchemaAnalyzer1->getSchema();
58+
$this->assertEquals($tableNames, $schemaFromAnalyser->getTableNames());
59+
$this->assertEquals($schemaFromAnalyser->getTableNames(), $schemaFromAnalyserCached->getTableNames());
60+
}
61+
4162
public function testGetSchema(): void
4263
{
4364
$schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), new ArrayCache(), 'prefix_');

0 commit comments

Comments
 (0)