Skip to content

Commit

Permalink
Support DBAL 4 and ORM 3 (#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Nov 24, 2023
1 parent b90bbf0 commit bbcb74f
Show file tree
Hide file tree
Showing 26 changed files with 181 additions and 94 deletions.
16 changes: 9 additions & 7 deletions composer.json
Expand Up @@ -19,19 +19,21 @@
"doctrine/persistence": "^2.0|^3.0"
},
"conflict": {
"doctrine/dbal": "<3",
"doctrine/orm": "<2.14",
"doctrine/dbal": "<3.5 || >=5",
"doctrine/orm": "<2.14 || >=4",
"doctrine/phpcr-odm": "<1.3.0"
},
"require-dev": {
"ext-sqlite3": "*",
"doctrine/coding-standard": "^11.1",
"doctrine/dbal": "^3.0",
"doctrine/annotations": "^1.12 || ^2",
"doctrine/coding-standard": "^12",
"doctrine/dbal": "^3.5 || ^4",
"doctrine/mongodb-odm": "^1.3.0 || ^2.0.0",
"doctrine/orm": "^2.14",
"doctrine/orm": "^2.14 || ^3",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6.6 || ^10.0",
"symfony/cache": "^5.4 || ^6.2",
"phpunit/phpunit": "^9.6.13 || ^10.4.2",
"symfony/cache": "^5.4 || ^6.3 || ^7",
"symfony/var-exporter": "^5.4 || ^6.3 || ^7",
"vimeo/psalm": "^5.9"
},
"suggest": {
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Expand Up @@ -5,5 +5,9 @@ parameters:
- src
- tests

ignoreErrors:
# ORM 3 forward compatibility
- '~^Parameter \$assoc of method Doctrine\\Common\\DataFixtures\\Purger\\ORMPurger\:\:getJoinTableName\(\) has invalid type Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\.$~'

includes:
- phpstan-baseline.neon
9 changes: 9 additions & 0 deletions psalm.xml
Expand Up @@ -16,4 +16,13 @@
<directory name="vendor" />
</ignoreFiles>
</projectFiles>

<issueHandlers>
<UndefinedDocblockClass>
<errorLevel type="suppress">
<!-- ORM 3 forward compatibility -->
<referencedClass name="Doctrine\ORM\Mapping\ManyToManyOwningSideMapping" />
</errorLevel>
</UndefinedDocblockClass>
</issueHandlers>
</psalm>
2 changes: 1 addition & 1 deletion src/AbstractFixture.php
Expand Up @@ -24,7 +24,7 @@ abstract class AbstractFixture implements SharedFixtureInterface
protected $referenceRepository;

/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setReferenceRepository(ReferenceRepository $referenceRepository)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Event/Listener/MongoDBReferenceListener.php
Expand Up @@ -24,7 +24,7 @@ public function __construct(ReferenceRepository $referenceRepository)
}

/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getSubscribedEvents(): array
{
Expand Down
6 changes: 3 additions & 3 deletions src/Event/Listener/ORMReferenceListener.php
Expand Up @@ -6,7 +6,7 @@

use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostPersistEventArgs;

use function get_class;

Expand All @@ -24,7 +24,7 @@ public function __construct(ReferenceRepository $referenceRepository)
}

/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function getSubscribedEvents(): array
{
Expand All @@ -35,7 +35,7 @@ public function getSubscribedEvents(): array
/**
* Populates identities for stored references
*/
public function postPersist(LifecycleEventArgs $args): void
public function postPersist(PostPersistEventArgs $args): void
{
$object = $args->getObject();

Expand Down
5 changes: 3 additions & 2 deletions src/Purger/ORMPurger.php
Expand Up @@ -9,6 +9,7 @@
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping;

use function array_map;
use function array_reverse;
Expand Down Expand Up @@ -256,9 +257,9 @@ private function getTableName(ClassMetadata $class, AbstractPlatform $platform):
return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
}

/** @param mixed[] $assoc */
/** @param ManyToManyOwningSideMapping|mixed[] $assoc */
private function getJoinTableName(
array $assoc,
$assoc,
ClassMetadata $class,
AbstractPlatform $platform
): string {
Expand Down
37 changes: 21 additions & 16 deletions tests/Common/DataFixtures/BaseTestCase.php
Expand Up @@ -9,33 +9,38 @@
use Doctrine\ORM\ORMSetup;
use PHPUnit\Framework\TestCase;

use function method_exists;

use const PHP_VERSION_ID;

/**
* Base test class
*/
abstract class BaseTestCase extends TestCase
{
/**
* EntityManager mock object together with
* annotation mapping driver
*/
protected function getMockAnnotationReaderEntityManager(): EntityManager
{
$dbParams = ['driver' => 'pdo_sqlite', 'memory' => true];
$config = ORMSetup::createAnnotationMetadataConfiguration([__DIR__ . '/TestEntity'], true);

return new EntityManager(DriverManager::getConnection($dbParams, $config), $config);
}

/**
* EntityManager mock object together with
* annotation mapping driver and pdo_sqlite
* database in memory
*/
protected function getMockSqliteEntityManager(): EntityManager
protected function getMockSqliteEntityManager(string $fixtureSet = 'TestEntity'): EntityManager
{
$dbParams = ['driver' => 'pdo_sqlite', 'memory' => true];
$config = ORMSetup::createAnnotationMetadataConfiguration([__DIR__ . '/TestEntity'], true);
$dbParams = ['driver' => 'sqlite3', 'memory' => true];
if (PHP_VERSION_ID >= 80100) {
$config = ORMSetup::createAttributeMetadataConfiguration([__DIR__ . '/' . $fixtureSet], true);
$config->setLazyGhostObjectEnabled(true);
} else {
$config = ORMSetup::createAnnotationMetadataConfiguration([__DIR__ . '/' . $fixtureSet], true);
}

$connection = DriverManager::getConnection($dbParams, $config);
$platform = $connection->getDatabasePlatform();
if (method_exists($platform, 'disableSchemaEmulation')) {
$platform->disableSchemaEmulation();
}

$connection->executeStatement('ATTACH DATABASE \':memory:\' AS readers');

return new EntityManager(DriverManager::getConnection($dbParams, $config), $config);
return new EntityManager($connection, $config);
}
}
Expand Up @@ -12,8 +12,6 @@
use Doctrine\Tests\Common\DataFixtures\TestEntity\User;
use PHPUnit\Framework\MockObject\MockObject;

use function extension_loaded;

/**
* Test referenced fixture execution
*/
Expand All @@ -24,7 +22,7 @@ class ORMExecutorSharedFixtureTest extends BaseTestCase

public function testFixtureExecution(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);

Expand All @@ -43,10 +41,6 @@ public function testFixtureExecution(): void

public function testSharedFixtures(): void
{
if (! extension_loaded('pdo_sqlite')) {
$this->markTestSkipped('Missing pdo_sqlite extension.');
}

$em = $this->getMockSqliteEntityManager();
$schemaTool = new SchemaTool($em);
$schemaTool->dropSchema([]);
Expand Down
17 changes: 9 additions & 8 deletions tests/Common/DataFixtures/Executor/ORMExecutorTest.php
Expand Up @@ -12,6 +12,7 @@
use Doctrine\Common\EventManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Tests\Common\DataFixtures\BaseTestCase;
use Doctrine\Tests\Mock\ForwardCompatibleEntityManager;
use PHPUnit\Framework\MockObject\MockObject;

/**
Expand All @@ -38,8 +39,8 @@ public function testExecuteSingleTransactionsCountTransactionalCalls(): void
{
$em = $this->getMockEntityManager();
$em->method('getEventManager')->willReturn($this->createMock(EventManager::class));
// We call transactional once for purge and for the fixtures (load)
$em->expects($this->once())->method('transactional')->with(self::isInstanceOf(Closure::class));
// We call wrapInTransaction once for purge and for the fixtures (load)
$em->expects($this->once())->method('wrapInTransaction')->with(self::isInstanceOf(Closure::class));

$executor = new ORMExecutor($em);
$fixture = $this->getMockFixture();
Expand All @@ -52,7 +53,7 @@ public function testExecuteWithPurge(): void
$purger = $this->getMockPurger();
$purger->expects($this->once())
->method('purge')
->will($this->returnValue(null));
->willReturn(null);
$executor = new ORMExecutor($em, $purger);
$fixture = $this->getMockFixture();
$fixture->expects($this->once())
Expand All @@ -76,7 +77,7 @@ public function testCustomLegacyEntityManager(): void
{
$em = $this->getMockEntityManager();
$em->method('getEventManager')->willReturn($this->createMock(EventManager::class));
$em->expects($this->once())->method('transactional')->with(self::isInstanceOf(Closure::class));
$em->expects($this->once())->method('wrapInTransaction')->with(self::isInstanceOf(Closure::class));

$executor = new ORMExecutor($em);
@$executor->execute([]);
Expand All @@ -103,7 +104,7 @@ public function testExecuteMultipleTransactionsWithPurge(): void
$purger = $this->getMockPurger();
$purger->expects($this->once())
->method('purge')
->will($this->returnValue(null));
->willReturn(null);
$executor = new MultipleTransactionORMExecutor($em, $purger);
$fixture = $this->getMockFixture();
$fixture->expects($this->once())
Expand All @@ -116,8 +117,8 @@ public function testExecuteMultipleTransactionsCountTransactionalCalls(): void
{
$em = $this->getMockEntityManager();
$em->method('getEventManager')->willReturn($this->createMock(EventManager::class));
// We call transactional once for purge and twice for the fixtures (load)
$em->expects($this->exactly(3))->method('transactional')->with(self::isInstanceOf(Closure::class));
// We call wrapInTransaction once for purge and twice for the fixtures (load)
$em->expects($this->exactly(3))->method('wrapInTransaction')->with(self::isInstanceOf(Closure::class));

$executor = new MultipleTransactionORMExecutor($em);
$fixture = $this->getMockFixture();
Expand All @@ -127,7 +128,7 @@ public function testExecuteMultipleTransactionsCountTransactionalCalls(): void
/** @return EntityManagerInterface&MockObject */
private function getMockEntityManager(): EntityManagerInterface
{
return $this->createMock(EntityManagerInterface::class);
return $this->createMock(ForwardCompatibleEntityManager::class);
}

/** @return FixtureInterface&MockObject */
Expand Down
2 changes: 1 addition & 1 deletion tests/Common/DataFixtures/ProxyReferenceRepositoryTest.php
Expand Up @@ -35,7 +35,7 @@ public static function setUpBeforeClass(): void

public function testReferenceEntry(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$role = new TestEntity\Role();
$role->setName('admin');
$meta = $em->getClassMetadata(self::TEST_ENTITY_ROLE);
Expand Down
11 changes: 1 addition & 10 deletions tests/Common/DataFixtures/Purger/ORMPurgerExcludeTest.php
Expand Up @@ -5,15 +5,12 @@
namespace Doctrine\Tests\Common\DataFixtures;

use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\Tests\Common\DataFixtures\TestPurgeEntity\ExcludedEntity;
use Doctrine\Tests\Common\DataFixtures\TestPurgeEntity\IncludedEntity;

use function count;
use function extension_loaded;
use function preg_match;

class ORMPurgerExcludeTest extends BaseTestCase
Expand All @@ -26,13 +23,7 @@ class ORMPurgerExcludeTest extends BaseTestCase
*/
protected function loadTestData(): EntityManager
{
if (! extension_loaded('pdo_sqlite')) {
$this->markTestSkipped('Missing pdo_sqlite extension.');
}

$dbParams = ['driver' => 'pdo_sqlite', 'memory' => true];
$config = ORMSetup::createAnnotationMetadataConfiguration([__DIR__ . '/../TestPurgeEntity'], true);
$em = new EntityManager(DriverManager::getConnection($dbParams, $config), $config);
$em = $this->getMockSqliteEntityManager('TestPurgeEntity');

$schemaTool = new SchemaTool($em);
$schemaTool->dropDatabase();
Expand Down
14 changes: 7 additions & 7 deletions tests/Common/DataFixtures/Purger/ORMPurgerTest.php
Expand Up @@ -20,20 +20,20 @@ class ORMPurgerTest extends BaseTestCase

public function testGetAssociationTables(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$metadata = $em->getClassMetadata(self::TEST_ENTITY_USER);
$platform = $em->getConnection()->getDatabasePlatform();
$purger = new ORMPurger($em);
$class = new ReflectionClass(ORMPurger::class);
$method = $class->getMethod('getAssociationTables');
$method->setAccessible(true);
$associationTables = $method->invokeArgs($purger, [[$metadata], $platform]);
$this->assertEquals($associationTables[0], 'readers__author_reader');
$this->assertEquals('readers.author_reader', $associationTables[0]);
}

public function testGetAssociationTablesQuoted(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$metadata = $em->getClassMetadata(self::TEST_ENTITY_QUOTED);
$platform = $em->getConnection()->getDatabasePlatform();
$purger = new ORMPurger($em);
Expand All @@ -46,7 +46,7 @@ public function testGetAssociationTablesQuoted(): void

public function testTableNameWithSchema(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$metadata = $em->getClassMetadata(self::TEST_ENTITY_USER_WITH_SCHEMA);
$platform = $em->getConnection()->getDatabasePlatform();
$purger = new ORMPurger($em);
Expand All @@ -59,7 +59,7 @@ public function testTableNameWithSchema(): void

public function testGetDeleteFromTableSQL(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$metadata = $em->getClassMetadata(self::TEST_ENTITY_GROUP);
$platform = $em->getConnection()->getDatabasePlatform();
$purger = new ORMPurger($em);
Expand All @@ -75,7 +75,7 @@ public function testGetDeleteFromTableSQL(): void

public function testGetDeleteFromTableSQLWithSchema(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();
$metadata = $em->getClassMetadata(self::TEST_ENTITY_GROUP_WITH_SCHEMA);
$platform = $em->getConnection()->getDatabasePlatform();
$purger = new ORMPurger($em);
Expand All @@ -86,6 +86,6 @@ public function testGetDeleteFromTableSQLWithSchema(): void
$method = $class->getMethod('getDeleteFromTableSQL');
$method->setAccessible(true);
$sql = $method->invokeArgs($purger, [$tableName, $platform]);
$this->assertEquals('DELETE FROM test_schema__group', $sql);
$this->assertEquals('DELETE FROM test_schema."group"', $sql);
}
}
8 changes: 4 additions & 4 deletions tests/Common/DataFixtures/ReferenceRepositoryTest.php
Expand Up @@ -7,19 +7,19 @@
use BadMethodCallException;
use Doctrine\Common\DataFixtures\Event\Listener\ORMReferenceListener;
use Doctrine\Common\DataFixtures\ReferenceRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Proxy;
use Doctrine\Tests\Common\DataFixtures\TestEntity\Role;
use Doctrine\Tests\Mock\ForwardCompatibleEntityManager;
use OutOfBoundsException;

class ReferenceRepositoryTest extends BaseTestCase
{
public function testReferenceEntry(): void
{
$em = $this->getMockAnnotationReaderEntityManager();
$em = $this->getMockSqliteEntityManager();

$role = new TestEntity\Role();
$role->setName('admin');
Expand Down Expand Up @@ -199,7 +199,7 @@ public function testGetIdentifierWhenHasNotBeenManagedYetByUnitOfWork(): void
->with($role)
->willReturn($identitiesExpected);

$em = $this->createMock(EntityManagerInterface::class);
$em = $this->createMock(ForwardCompatibleEntityManager::class);
$em->method('getUnitOfWork')
->willReturn($uow);
$em->method('getClassMetadata')
Expand Down

0 comments on commit bbcb74f

Please sign in to comment.