Skip to content

Commit

Permalink
added a schema lock file to tdbm
Browse files Browse the repository at this point in the history
  • Loading branch information
Kharhamel committed Sep 6, 2019
1 parent 42e2f4f commit fb45e33
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 137 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ vendor/*
/.travis/
/vendor-bin/require-checker/vendor/
/vendor-bin/couscous/vendor/
.phpunit.result.cache
.phpunit.result.cache
.php_cs.cache
tdbm.lock.yml
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"ext-PDO": "*",
"ext-json": "*",
"ext-hash": "*",
"ext-filter": "*"
"ext-filter": "*",
"brain-diminished/schema-version-control": "^1.0"
},
"require-dev" : {
"phpunit/phpunit": "^7.4.4 || ^8.0.0",
Expand Down
55 changes: 24 additions & 31 deletions src/TDBMSchemaAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace TheCodingMachine\TDBM;

use BrainDiminished\SchemaVersionControl\SchemaVersionControlService;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Column;
Expand All @@ -12,12 +13,16 @@
use Doctrine\DBAL\Types\DateType;
use Doctrine\DBAL\Types\Type;
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
use TheCodingMachine\TDBM\Utils\ImmutableCaster;
use TheCodingMachine\TDBM\Utils\RootProjectLocator;

/**
* This class is used to analyze the schema and return valuable information / hints.
*/
class TDBMSchemaAnalyzer
{
const schemaFileName = 'tdbm.lock.yml';

private $connection;

/**
Expand All @@ -40,6 +45,11 @@ class TDBMSchemaAnalyzer
*/
private $schemaAnalyzer;

/**
* @var SchemaVersionControlService
*/
private $schemaVersionControlService;

/**
* @param Connection $connection The DBAL DB connection to use
* @param Cache $cache A cache service to be used
Expand All @@ -51,6 +61,7 @@ public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer
$this->connection = $connection;
$this->cache = $cache;
$this->schemaAnalyzer = $schemaAnalyzer;
$this->schemaVersionControlService = new SchemaVersionControlService($this->connection, self::getLockFilePath());
}

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

public static function getLockFilePath(): string
{
return RootProjectLocator::getRootLocationPath().self::schemaFileName;
}

/**
* Returns the (cached) schema.
*
* @return Schema
*/
public function getSchema(): Schema
public function getSchema(bool $ignoreCache = false): Schema
{
if ($this->schema === null) {
$cacheKey = $this->getCachePrefix().'_immutable_schema';
if ($this->cache->contains($cacheKey)) {
if (!$ignoreCache && $this->cache->contains($cacheKey)) {
$this->schema = $this->cache->fetch($cacheKey);
} elseif (!file_exists(self::getLockFilePath())) {
throw new TDBMException('No tdbm lock file found. Please regenerate DAOs and Beans.');
} else {
$this->schema = $this->connection->getSchemaManager()->createSchema();
$this->castSchemaToImmutable($this->schema);
$this->schema = $this->schemaVersionControlService->loadSchemaFile();
ImmutableCaster::castSchemaToImmutable($this->schema);
$this->cache->save($cacheKey, $this->schema);
}
}

return $this->schema;
}

private function castSchemaToImmutable(Schema $schema): void
public function generateLockFile(): void
{
foreach ($schema->getTables() as $table) {
foreach ($table->getColumns() as $column) {
$this->toImmutableType($column);
}
}
}

/**
* Changes the type of a column to an immutable date type if the type is a date.
* This is needed because by default, when reading a Schema, Doctrine assumes a mutable datetime.
*/
private function toImmutableType(Column $column): void
{
$mapping = [
Type::DATE => Type::DATE_IMMUTABLE,
Type::DATETIME => Type::DATETIME_IMMUTABLE,
Type::DATETIMETZ => Type::DATETIMETZ_IMMUTABLE,
Type::TIME => Type::TIME_IMMUTABLE
];

$typeName = $column->getType()->getName();
if (isset($mapping[$typeName])) {
$column->setType(Type::getType($mapping[$typeName]));
}
$this->schemaVersionControlService->dumpSchema();
}

/**
Expand Down
38 changes: 38 additions & 0 deletions src/Utils/ImmutableCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace TheCodingMachine\TDBM\Utils;

use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;

class ImmutableCaster
{
public static function castSchemaToImmutable(Schema $schema): void
{
foreach ($schema->getTables() as $table) {
foreach ($table->getColumns() as $column) {
self::toImmutableType($column);
}
}
}

/**
* Changes the type of a column to an immutable date type if the type is a date.
* This is needed because by default, when reading a Schema, Doctrine assumes a mutable datetime.
*/
private static function toImmutableType(Column $column): void
{
$mapping = [
Type::DATE => Type::DATE_IMMUTABLE,
Type::DATETIME => Type::DATETIME_IMMUTABLE,
Type::DATETIMETZ => Type::DATETIMETZ_IMMUTABLE,
Type::TIME => Type::TIME_IMMUTABLE
];

$typeName = $column->getType()->getName();
if (isset($mapping[$typeName])) {
$column->setType(Type::getType($mapping[$typeName]));
}
}
}
13 changes: 13 additions & 0 deletions src/Utils/RootProjectLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php


namespace TheCodingMachine\TDBM\Utils;

class RootProjectLocator
{
public static function getRootLocationPath(): string
{
$reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class);
return dirname($reflection->getFileName(), 3).'/';
}
}
14 changes: 5 additions & 9 deletions src/Utils/TDBMDaoGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@
*/
class TDBMDaoGenerator
{
/**
* @var Schema
*/
private $schema;

/**
* @var TDBMSchemaAnalyzer
*/
Expand Down Expand Up @@ -66,7 +61,6 @@ class TDBMDaoGenerator
public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
{
$this->configuration = $configuration;
$this->schema = $tdbmSchemaAnalyzer->getSchema();
$this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
$this->namingStrategy = $configuration->getNamingStrategy();
$this->eventDispatcher = $configuration->getGeneratorEventDispatcher();
Expand All @@ -79,9 +73,11 @@ public function __construct(ConfigurationInterface $configuration, TDBMSchemaAna
*/
public function generateAllDaosAndBeans(): void
{
// TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
$this->tdbmSchemaAnalyzer->generateLockFile();
$schema = $this->tdbmSchemaAnalyzer->getSchema();

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

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

$beanDescriptors = [];

$beanRegistry = new BeanRegistry($this->configuration, $this->schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy);
$beanRegistry = new BeanRegistry($this->configuration, $schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy);
foreach ($tableList as $table) {
$beanDescriptors[] = $beanRegistry->addBeanForTable($table);
}
Expand Down
8 changes: 8 additions & 0 deletions tests/Commands/GenerateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use TheCodingMachine\TDBM\TDBMSchemaAnalyzer;

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

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

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

/**
Expand Down
9 changes: 9 additions & 0 deletions tests/TDBMDaoGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,19 @@ public function testDaoGeneration(): void
touch($dummyFile);
$this->assertFileExists($dummyFile);

//let's delete the lock file
$schemaFilePath = TDBMSchemaAnalyzer::getLockFilePath();
if (file_exists($schemaFilePath)) {
unlink($schemaFilePath);
}

$this->tdbmDaoGenerator->generateAllDaosAndBeans();

$this->assertFileNotExists($dummyFile);

//Check that the lock file was generated
$this->assertFileExists($schemaFilePath);

// Let's require all files to check they are valid PHP!
// Test the daoFactory
require_once $this->rootPath . 'src/Test/Dao/Generated/DaoFactory.php';
Expand Down
21 changes: 21 additions & 0 deletions tests/TDBMSchemaAnalyzerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

use Doctrine\Common\Cache\ArrayCache;
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
use TheCodingMachine\TDBM\Utils\ImmutableCaster;

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

public function testSchemaLock(): void
{
$schemaFromConnec = self::getConnection()->getSchemaManager()->createSchema();
$tableNames = [];
//lock file doesn't save the database name so we have to replace it manually.
foreach ($schemaFromConnec->getTableNames() as $tableName) {
$tableNames[] = str_replace('tdbm_testcase', 'public', $tableName);
}
ImmutableCaster::castSchemaToImmutable($schemaFromConnec);

$schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), new ArrayCache(), 'prefix_');
$cache = new ArrayCache();
$tdbmSchemaAnalyzer1 = new TDBMSchemaAnalyzer(self::getConnection(), $cache, $schemaAnalyzer);

$schemaFromAnalyser = $tdbmSchemaAnalyzer1->getSchema(true);
$schemaFromAnalyserCached = $tdbmSchemaAnalyzer1->getSchema();
$this->assertEquals($tableNames, $schemaFromAnalyser->getTableNames());
$this->assertEquals($schemaFromAnalyser->getTableNames(), $schemaFromAnalyserCached->getTableNames());
}

public function testGetSchema(): void
{
$schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), new ArrayCache(), 'prefix_');
Expand Down
Loading

0 comments on commit fb45e33

Please sign in to comment.