Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make "database reset" mechanism extendable #690

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor(reset database): for mongo
nikophil committed Oct 22, 2024
commit 2c78963da3f2a4a5f788eff088e79d5b5fb93812
4 changes: 2 additions & 2 deletions bin/console
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@
<?php

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Zenstruck\Foundry\Tests\Fixtures\Kernel;
use Zenstruck\Foundry\Tests\Fixture\TestKernel;

require_once __DIR__ . '/../tests/bootstrap.php';

$application = new Application(new Kernel('test', true));
$application = new Application(new TestKernel('test', true));
$application->run();
6 changes: 6 additions & 0 deletions config/mongo.php
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Zenstruck\Foundry\Mongo\MongoPersistenceStrategy;
use Zenstruck\Foundry\Mongo\MongoSchemaResetter;

return static function (ContainerConfigurator $container): void {
$container->services()
@@ -12,5 +13,10 @@
abstract_arg('config'),
])
->tag('.foundry.persistence_strategy')
->set('.zenstruck_foundry.persistence.schema_resetter.mongo', MongoSchemaResetter::class)
->args([
abstract_arg('managers'),
])
->tag('.foundry.persistence.schema_resetter')
;
};
1 change: 1 addition & 0 deletions config/persistence.php
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
->set('.zenstruck_foundry.persistence_manager', PersistenceManager::class)
->args([
tagged_iterator('.foundry.persistence_strategy'),
tagged_iterator('.foundry.persistence.schema_resetter'),
])
;
};
51 changes: 51 additions & 0 deletions src/Mongo/MongoSchemaResetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Mongo;

use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Persistence\ResetDatabase\SchemaResetterInterface;
use Zenstruck\Foundry\Persistence\SymfonyCommandRunner;

/**
* @internal
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
final class MongoSchemaResetter implements SchemaResetterInterface
{
use SymfonyCommandRunner;

/**
* @param list<string> $managers
*/
public function __construct(private array $managers)
{
}

public function resetSchema(KernelInterface $kernel): void
{
$application = self::application($kernel);

foreach ($this->managers as $manager) {
try {
self::runCommand(
$application,
'doctrine:mongodb:schema:drop',
[
'--dm' => $manager,
]
);
} catch (\Exception) {
}

self::runCommand(
$application,
'doctrine:mongodb:schema:create',
[
'--dm' => $manager,
]
);
}
}
}
47 changes: 47 additions & 0 deletions src/ORM/OrmDatabaseResetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\ORM;

use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Persistence\ResetDatabase\DatabaseResetterInterface;
use Zenstruck\Foundry\Persistence\SymfonyCommandRunner;

/**
* @internal
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
final class OrmDatabaseResetter implements DatabaseResetterInterface
{
use OrmDatabaseResetterTrait;
use SymfonyCommandRunner;

/**
* @param list<string> $managers
* @param list<string> $connections
*/
public function __construct(
private array $managers,
private array $connections,
) {
}

final public function resetDatabase(KernelInterface $kernel): void
{
$application = self::application($kernel);

$this->dropAndResetDatabase($application);
$this->createSchema($application);
}

private function managers(): array
{
return $this->managers;
}

private function connections(): array
{
return $this->connections;
}
}
76 changes: 76 additions & 0 deletions src/ORM/OrmDatabaseResetterTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\ORM;

use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SQLitePlatform;
use Symfony\Bundle\FrameworkBundle\Console\Application;

/**
* @internal
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
trait OrmDatabaseResetterTrait
{
private function dropAndResetDatabase(Application $application): void
{
foreach ($this->connections() as $connection) {
$databasePlatform = $this->registry->getConnection($connection)->getDatabasePlatform(); // @phpstan-ignore-line

if ($databasePlatform instanceof SQLitePlatform) {
// we don't need to create the sqlite database - it's created when the schema is created
continue;
}

if ($databasePlatform instanceof PostgreSQLPlatform) {
// let's drop all connections to the database to be able to drop it
self::runCommand(
$application,
'dbal:run-sql',
[
'--connection' => $connection,
'sql' => 'SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()',
],
canFail: true,
);
}

self::runCommand($application, 'doctrine:database:drop', [
'--connection' => $connection,
'--force' => true,
'--if-exists' => true,
]);
self::runCommand($application, 'doctrine:database:create', ['--connection' => $connection]);
}
}

private function createSchema(Application $application): void
{
if (self::RESET_MODE_MIGRATE === $this->config['reset']['mode']) {
self::runCommand($application, 'doctrine:migrations:migrate', [
'--no-interaction' => true,
]);

return;
}

foreach ($this->managers() as $manager) {
self::runCommand($application, 'doctrine:schema:update', [
'--em' => $manager,
'--force' => true,
]);
}
}

/**
* @return list<string>
*/
abstract private function managers(): array;

/**
* @return list<string>
*/
abstract private function connections(): array;
}
32 changes: 32 additions & 0 deletions src/ORM/OrmMigrateSchemaResetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\ORM;

use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Persistence\PersistenceManager;
use Zenstruck\Foundry\Persistence\ResetDatabase\SchemaResetterInterface;
use Zenstruck\Foundry\Persistence\SymfonyCommandRunner;

/**
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
final class OrmMigrateSchemaResetter implements SchemaResetterInterface
{
use OrmDatabaseResetterTrait;
use SymfonyCommandRunner;

public function resetSchema(KernelInterface $kernel): void
{
if (PersistenceManager::isDAMADoctrineTestBundleEnabled()) {
// not required as the DAMADoctrineTestBundle wraps each test in a transaction
return;
}

$application = self::application($kernel);

$this->dropAndResetDatabase($application);
$this->createSchema($application);
}
}
66 changes: 66 additions & 0 deletions src/ORM/OrmSchemaResetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\ORM;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Persistence\PersistenceManager;
use Zenstruck\Foundry\Persistence\ResetDatabase\DatabaseResetterInterface;
use Zenstruck\Foundry\Persistence\ResetDatabase\SchemaResetterInterface;
use Zenstruck\Foundry\Persistence\SymfonyCommandRunner;

/**
* @internal
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
final class OrmSchemaResetter implements SchemaResetterInterface
{
use OrmDatabaseResetterTrait;
use SymfonyCommandRunner;

/**
* @param list<string> $managers
* @param list<string> $connections
*/
public function __construct(
private array $managers,
private array $connections,
) {
}

final public function resetSchema(KernelInterface $kernel): void
{
if (PersistenceManager::isDAMADoctrineTestBundleEnabled()) {
// not required as the DAMADoctrineTestBundle wraps each test in a transaction
return;
}

$application = self::application($kernel);

$this->dropSchema($application);
$this->createSchema($application);
}

private function dropSchema(Application $application): void
{
foreach ($this->managers() as $manager) {
self::runCommand($application, 'doctrine:schema:drop', [
'--em' => $manager,
'--force' => true,
'--full-database' => true,
]);
}
}

private function managers(): array
{
return $this->managers;
}

private function connections(): array
{
return $this->connections;
}
}
9 changes: 7 additions & 2 deletions src/Persistence/PersistenceManager.php
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@
use Zenstruck\Foundry\ORM\AbstractORMPersistenceStrategy;
use Zenstruck\Foundry\Persistence\Exception\NoPersistenceStrategy;
use Zenstruck\Foundry\Persistence\Exception\RefreshObjectFailed;
use Zenstruck\Foundry\Persistence\ResetDatabase\DatabaseResetterInterface;
use Zenstruck\Foundry\Persistence\ResetDatabase\SchemaResetterInterface;
use Zenstruck\Foundry\Tests\Fixture\TestKernel;

/**
@@ -38,9 +40,12 @@ final class PersistenceManager

/**
* @param PersistenceStrategy[] $strategies
* @param SchemaResetterInterface[] $schemaResetters
*/
public function __construct(private iterable $strategies)
{
public function __construct(
private iterable $strategies,
public iterable $schemaResetters,
) {
}

public static function isDAMADoctrineTestBundleEnabled(): bool
5 changes: 2 additions & 3 deletions src/Persistence/PersistenceStrategy.php
Original file line number Diff line number Diff line change
@@ -20,13 +20,14 @@
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Persistence\ResetDatabase\DatabaseResetterInterface;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*
* @internal
*/
abstract class PersistenceStrategy
abstract class PersistenceStrategy implements DatabaseResetterInterface
{
/**
* @param array<string,mixed> $config
@@ -89,8 +90,6 @@ abstract public function hasChanges(object $object): bool;

abstract public function contains(object $object): bool;

abstract public function resetDatabase(KernelInterface $kernel): void;

abstract public function resetSchema(KernelInterface $kernel): void;

abstract public function truncate(string $class): void;
12 changes: 12 additions & 0 deletions src/Persistence/ResetDatabase/DatabaseResetterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Persistence\ResetDatabase;

use Symfony\Component\HttpKernel\KernelInterface;

interface DatabaseResetterInterface
nikophil marked this conversation as resolved.
Show resolved Hide resolved
{
public function resetDatabase(KernelInterface $kernel): void;
}
Loading