Skip to content

Commit

Permalink
impl
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Apr 28, 2023
1 parent acd3249 commit 457eb7c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 52 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cache
*.cache.*

/demos/db.php
/demos/db-behat-rw.txt
/demos/_demo-data/db.sqlite
/demos/_demo-data/db.sqlite-journal
/phpunit.xml
Expand Down
20 changes: 18 additions & 2 deletions demos/init-db.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@

trait ModelPreventModificationTrait
{
protected function allowDbModifications(): bool
{
static $rw = null;
if ($rw === null) {
$rw = file_exists(__DIR__ . '/db-behat-rw.txt');
}

return $rw;
}

public function atomic(\Closure $fx)
{
$eRollback = new \Exception('Prevent modification');
Expand All @@ -34,7 +44,9 @@ public function atomic(\Closure $fx)
parent::atomic(function () use ($fx, $eRollback, &$res) {
$res = $fx();

throw $eRollback;
if (!$this->allowDbModifications()) {
throw $eRollback;
}
});
} catch (\Exception $e) {
if ($e !== $eRollback) {
Expand All @@ -60,7 +72,11 @@ protected function wrapUserActionCallbackPreventModification(Model\UserAction $a
$callbackBackup = $action->callback;
try {
$action->callback = $originalCallback;
$action->execute(...$args);
$res = $action->execute(...$args);

if ($this->allowDbModifications()) {
return $res;
}
} finally {
$action->callback = $callbackBackup;
}
Expand Down
116 changes: 66 additions & 50 deletions src/Behat/RwDemosContextTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ trait RwDemosContextTrait
'multiline_delivery',
];

/** @var array<string, array{ Model, array<int, array<string, mixed>> }>|null */
/** @var array<string, Model>|null */
protected ?array $databaseBackupModels = null;

/** @var array<string, array<int, array<string, mixed>>>|null */
protected ?array $databaseBackupData = null;

protected function getDemosDb(): Persistence\Sql
Expand All @@ -44,7 +47,7 @@ protected function getDemosDb(): Persistence\Sql
return $db;
}

protected function createModelFromTable(string $table): Model
protected function createDatabaseModelsSingle(string $table): Model
{
$db = $this->getDemosDb();
$schemaManager = $db->getConnection()->createSchemaManager();
Expand All @@ -63,85 +66,97 @@ protected function createModelFromTable(string $table): Model
return $model;
}

protected function createDatabaseModels(): void
{
$models = [];
foreach ($this->databaseBackupTables as $table) {
$models[$table] = $this->createDatabaseModelsSingle($table);
}

$this->databaseBackupModels = $models;
}

protected function createDatabaseBackup(): void
{
$backup = [];
$dataByTable = [];
foreach ($this->databaseBackupTables as $table) {
$model = $this->createModelFromTable($table);
$model = $this->databaseBackupModels[$table];

$tableData = [];
$data = [];
foreach ($model as $entity) {
$tableData[$entity->getId()] = $entity->get();
$data[$entity->getId()] = $entity->get();
}

$backup[$table] = [$model, $tableData];
$dataByTable[$table] = $data;
}

$this->databaseBackupData = $backup;
$this->databaseBackupData = $dataByTable;
}

/**
* @return \stdClass&object{ addedIds: list<int>, changedIds: list<int>, deletedIds: list<int> }
* @return array<string, \stdClass&object{ addedIds: list<int>, changedIds: list<int>, deletedIds: list<int> }>
*/
protected function discoverTableChanges(string $table): \stdClass
protected function discoverDatabaseChanges(): array
{
[$model, $tableData] = $this->databaseBackupData[$table];

$res = new \stdClass();
$res->addedIds = [];
$res->changedIds = [];
$res->deletedIds = array_fill_keys(array_keys($tableData), true);
foreach ($model as $entity) {
$id = $entity->getId();
if (!isset($tableData[$id])) {
$res->addedIds[] = $id;
} else {
$isChanged = false;
foreach ($tableData[$id] as $k => $v) {
if (!$entity->compare($k, $v)) {
$isChanged = true;

break;
$changesByTable = [];
foreach ($this->databaseBackupTables as $table) {
$model = $this->databaseBackupModels[$table];
$data = $this->databaseBackupData[$table];

$changes = new \stdClass();
$changes->addedIds = [];
$changes->changedIds = [];
$changes->deletedIds = array_fill_keys(array_keys($data), true);
foreach ($model as $entity) {
$id = $entity->getId();
if (!isset($data[$id])) {
$changes->addedIds[] = $id;
} else {
$isChanged = false;
foreach ($data[$id] as $k => $v) {
if (!$entity->compare($k, $v)) {
$isChanged = true;

break;
}
}

if ($isChanged) {
$changes->changedIds[] = $id;
}
}

if ($isChanged) {
$res->changedIds[] = $id;
unset($changes->deletedIds[$id]);
}
}
$changes->deletedIds = array_keys($changes->deletedIds);

unset($res->deletedIds[$id]);
if (count($changes->addedIds) > 0 || count($changes->changedIds) > 0 || count($changes->deletedIds) > 0) {
$changesByTable[$table] = $changes;
}
}
$res->deletedIds = array_keys($res->deletedIds);

return $res; // @phpstan-ignore-line https://github.com/phpstan/phpstan/issues/9252
return $changesByTable; // @phpstan-ignore-line https://github.com/phpstan/phpstan/issues/9252
}

protected function restoreDatabaseBackup(): void
{
$changesByTable = [];
$anyChange = false;
foreach ($this->databaseBackupData as $table => [$model, $tableData]) {
$changes = $this->discoverTableChanges($table);
$changesByTable[$table] = $changes;
if (count($changes->addedIds) > 0 || count($changes->changedIds) > 0 || count($changes->deletedIds) > 0) {
$anyChange = true;
}
}
unset($table);
$changesByTable = $this->discoverDatabaseChanges();

if ($anyChange) {
if (count($changesByTable) > 0) {
// TODO disable FK checks
// unfortunately there is no DBAL API - https://github.com/doctrine/dbal/pull/2620
try {
foreach ($changesByTable as $table => $changes) {
[$model, $tableData] = $this->databaseBackupData[$table];
$model = $this->databaseBackupModels[$table];
$data = $this->databaseBackupData[$table];

foreach ($changes->addedIds as $id) {
$model->delete($id);
}

foreach ([...$changes->changedIds, ...$changes->deletedIds] as $id) {
$entity = in_array($id, $changes->changedIds, true) ? $model->load($id) : $model->createEntity();
$entity->setMulti($tableData[$id]);
$entity->setMulti($data[$id]);
$entity->save();
}
}
Expand All @@ -156,10 +171,10 @@ protected function restoreDatabaseBackup(): void
*/
public function restoreDatabase(): void
{
// TODO disable RW demos

if ($this->needDatabaseRestore) {
$this->needDatabaseRestore = false;
unlink($this->demosDir . '/db-behat-rw.txt');

$this->restoreDatabaseBackup();
}
}
Expand All @@ -170,10 +185,11 @@ public function restoreDatabase(): void
public function allowDbModifications(): void
{
if ($this->databaseBackupData === null) {
$this->createDatabaseModels();
$this->createDatabaseBackup();
}
$this->needDatabaseRestore = true;

// TODO enable RW demos
$this->needDatabaseRestore = true;
file_put_contents($this->demosDir . '/db-behat-rw.txt', '1');
}
}

0 comments on commit 457eb7c

Please sign in to comment.