Skip to content

Commit d254650

Browse files
committed
feat: implement caching for PostgreSQL
1 parent 2b810cd commit d254650

File tree

13 files changed

+328
-3
lines changed

13 files changed

+328
-3
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ RUN apt-get update \
88
unzip \
99
wget \
1010
default-mysql-client \
11+
postgresql-client \
1112
&& docker-php-ext-install \
1213
pdo_mysql \
1314
pdo_pgsql \

doc/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ liip_test_fixtures:
1717
- `cache_db`: an array with a storage as key and a service as value, examples :
1818
- `sqlite: 'Liip\TestFixturesBundle\Services\DatabaseBackup\SqliteDatabaseBackup'`
1919
- `mysql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MysqlDatabaseBackup'`
20+
- `pgsql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup'`
2021
- `mongodb: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MongodbDatabaseBackup'`
2122

2223
« [Installation](./installation.md) • [Database](./database.md) »

doc/database.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ For example:
134134
liip_test_fixtures:
135135
cache_db:
136136
mysql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MysqlDatabaseBackup'
137+
pgsql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup'
137138
mongodb: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MongodbDatabaseBackup'
138139
phpcr: ...
139140
db2: ...
@@ -142,6 +143,8 @@ liip_test_fixtures:
142143

143144
**Attention: `Liip\TestFixturesBundle\Services\DatabaseBackup\MysqlDatabaseBackup` requires `mysql-client` installed on server.**
144145

146+
**Attention: `Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup` requires `postgresql-client` installed on server.**
147+
145148
**Attention: `Liip\TestFixturesBundle\Services\DatabaseBackup\MongodbDatabaseBackup` requires `mongodb-clients` installed on server.**
146149

147150
### Load fixtures ([↑](#methods))

src/Resources/config/database_tools.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
<argument type="service" id="Liip\TestFixturesBundle\Services\FixturesLoaderFactory" />
2525
</service>
2626

27+
<service id="Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup" public="true">
28+
<argument type="service" id="service_container" />
29+
<argument type="service" id="Liip\TestFixturesBundle\Services\FixturesLoaderFactory" />
30+
</service>
31+
2732
<service id="Liip\TestFixturesBundle\Services\DatabaseBackup\MongodbDatabaseBackup" public="true">
2833
<argument type="service" id="service_container" />
2934
<argument type="service" id="Liip\TestFixturesBundle\Services\MongoDBFixturesLoaderFactory" />
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Liip/TestFixturesBundle
7+
*
8+
* (c) Lukas Kahwe Smith <[email protected]>
9+
*
10+
* This source file is subject to the MIT license that is bundled
11+
* with this source code in the file LICENSE.
12+
*/
13+
14+
namespace Liip\TestFixturesBundle\Services\DatabaseBackup;
15+
16+
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor;
17+
use Doctrine\Common\DataFixtures\ProxyReferenceRepository;
18+
use Doctrine\ORM\EntityManager;
19+
20+
/**
21+
* @author Nikita Slutsky <[email protected]>
22+
*/
23+
final class PgsqlDatabaseBackup extends AbstractDatabaseBackup
24+
{
25+
public function getBackupFilePath(): string
26+
{
27+
$cacheDir = $this->container->getParameter('kernel.cache_dir');
28+
$hash = md5(serialize($this->metadatas).serialize($this->classNames));
29+
30+
return $cacheDir.'/test_postgresql_'.$hash.'.tar';
31+
}
32+
33+
public function isBackupActual(): bool
34+
{
35+
$backupFileName = $this->getBackupFilePath();
36+
$backupReferenceFileName = $backupFileName.'.ser';
37+
38+
return file_exists($backupFileName)
39+
&& file_exists($backupReferenceFileName)
40+
&& $this->isBackupUpToDate($backupFileName);
41+
}
42+
43+
public function backup(AbstractExecutor $executor): void
44+
{
45+
/** @var ProxyReferenceRepository $referenceRepository */
46+
$referenceRepository = $executor->getReferenceRepository();
47+
48+
/** @var EntityManager $em */
49+
$em = $referenceRepository->getManager();
50+
51+
$connection = $em->getConnection();
52+
$params = $connection->getParams();
53+
54+
$referenceRepository->save($this->getBackupFilePath());
55+
exec($this->createCommand('pg_dump --format=t', $params).' > '.$this->getBackupFilePath());
56+
}
57+
58+
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
59+
{
60+
/** @var ProxyReferenceRepository $referenceRepository */
61+
$referenceRepository = $executor->getReferenceRepository();
62+
63+
/** @var EntityManager $em */
64+
$em = $referenceRepository->getManager();
65+
66+
$connection = $em->getConnection();
67+
$params = $connection->getParams();
68+
69+
$this->executeQuery($connection, 'SET session_replication_role = \'replica\';');
70+
exec($this->createCommand('pg_restore --format=t --clean', $params).' '.$this->getBackupFilePath());
71+
$this->executeQuery($connection, 'SET session_replication_role = \'origin\';');
72+
$referenceRepository->load($this->getBackupFilePath());
73+
}
74+
75+
protected function getReferenceBackup(): string
76+
{
77+
return file_get_contents($this->getBackupFilePath());
78+
}
79+
80+
private function createCommand(string $command, array $params): string
81+
{
82+
// doctrine-bundle >= 2.2
83+
if (isset($params['primary'])) {
84+
$params = $params['primary'];
85+
}
86+
// doctrine-bundle < 2.2
87+
elseif (isset($params['master'])) {
88+
$params = $params['master'];
89+
}
90+
91+
$command .= isset($params['dbname']) && $params['dbname'] ? ' --dbname='.$params['dbname'] : '';
92+
$command .= isset($params['host']) && $params['host'] ? ' --host='.$params['host'] : '';
93+
$command .= isset($params['port']) && $params['port'] ? ' --port='.$params['port'] : '';
94+
$command .= isset($params['user']) && $params['user'] ? ' --username='.$params['user'] : '';
95+
96+
if (isset($params['password']) && $params['password']) {
97+
$command = 'PGPASSWORD='.$params['password'].' '.$command.' --no-password';
98+
}
99+
100+
return $command;
101+
}
102+
103+
private function executeQuery($connection, string $query): void
104+
{
105+
if (method_exists($connection, 'executeQuery')) {
106+
$connection->executeQuery($query);
107+
} else {
108+
$connection->query($query);
109+
}
110+
}
111+
}

src/Services/DatabaseTools/ORMDatabaseTool.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public function loadFixtures(array $classNames = [], bool $append = false): Abst
9898

9999
return $executor;
100100
}
101+
102+
$this->om->clear();
101103
}
102104

103105
// TODO: handle case when using persistent connections. Fail loudly?

tests/AppConfig/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ liip_test_fixtures:
1010
cache_db:
1111
sqlite: 'Liip\TestFixturesBundle\Services\DatabaseBackup\SqliteDatabaseBackup'
1212
mysql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MysqlDatabaseBackup'
13+
pgsql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup'
1314
mongodb: 'Liip\TestFixturesBundle\Services\DatabaseBackup\MongodbDatabaseBackup'
1415

1516
services:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Liip/TestFixturesBundle
7+
*
8+
* (c) Lukas Kahwe Smith <[email protected]>
9+
*
10+
* This source file is subject to the MIT license that is bundled
11+
* with this source code in the file LICENSE.
12+
*/
13+
14+
namespace Liip\Acme\Tests\AppConfigPgsqlCacheDb;
15+
16+
use Liip\Acme\Tests\AppConfigPgsql\AppConfigPgsqlKernel;
17+
use Symfony\Component\Config\Loader\LoaderInterface;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
20+
class AppConfigPgsqlKernelCacheDb extends AppConfigPgsqlKernel
21+
{
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public function getCacheDir(): string
26+
{
27+
return __DIR__.'/var/cache/';
28+
}
29+
30+
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
31+
{
32+
// Load the default file.
33+
parent::configureContainer($container, $loader);
34+
35+
// Load the file with specific configuration
36+
$loader->load(__DIR__.'/config.yml');
37+
}
38+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# inherits configuration from ../AppConfigMysql/config.yml
2+
3+
liip_test_fixtures:
4+
cache_db:
5+
pgsql: 'Liip\TestFixturesBundle\Services\DatabaseBackup\PgsqlDatabaseBackup'

tests/Test/ConfigMysqlCacheDbTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ public function testLoadFixturesAndCheckBackup(): void
118118
*/
119119
public function testLoadFixturesCheckReferences(): void
120120
{
121-
$this->markTestSkipped('This test is broken right now.');
122121
$referenceRepository = $this->databaseTool->loadFixtures([
123122
'Liip\Acme\Tests\App\DataFixtures\ORM\LoadUserData',
124123
])->getReferenceRepository();

0 commit comments

Comments
 (0)