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

Ensure Compatibility with Doctrine ORM 3 While Retaining Support for ORM 2 #60

Merged
merged 2 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion Entity/LibrariesHubCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity()]
#[ORM\Entity]
#[ORM\Table('h5p_libraries_hub_cache')]
class LibrariesHubCache
{
Expand Down
20 changes: 15 additions & 5 deletions Entity/LibrariesLanguagesRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NoResultException;
use Doctrine\Persistence\ManagerRegistry;
use Studit\H5PBundle\Service\DoctrineParser;

/**
* LibrariesLanguagesRepository
*/
class LibrariesLanguagesRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
public function __construct(ManagerRegistry $registry, private readonly DoctrineParser $parser)
{
parent::__construct($registry, LibrariesLanguages::class);
}
Expand All @@ -22,10 +23,15 @@ public function findForLibrary($machineName, $majorVersion, $minorVersion, $lang
->select('ll.languageJson')
->join('ll.library', 'l', 'WITH', 'l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion')
->where('ll.languageCode = :languageCode')
->setParameters(['majorVersion' => $majorVersion, 'machineName' => $machineName, 'minorVersion' => $minorVersion, 'languageCode' => $languageCode]);
->setParameters($this->parser->buildParams([
'majorVersion' => $majorVersion,
'machineName' => $machineName,
'minorVersion' => $minorVersion,
'languageCode' => $languageCode
]));
try {
$result = $qb->getQuery()->getSingleResult();
} catch (NoResultException $e) {
} catch (NoResultException) {
return null;
}
return $result['languageJson'] ? $result['languageJson'] : null;
Expand All @@ -35,10 +41,14 @@ public function findForLibraryAllLanguages($machineName, $majorVersion, $minorVe
$qb = $this->createQueryBuilder('ll')
->select('ll.languageCode')
->join('ll.library', 'l', 'WITH', 'l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion')
->setParameters(['majorVersion' => $majorVersion, 'machineName' => $machineName, 'minorVersion' => $minorVersion]);
->setParameters($this->parser->buildParams([
'majorVersion' => $majorVersion,
'machineName' => $machineName,
'minorVersion' => $minorVersion
]));
try {
$results = $qb->getQuery()->getArrayResult();
} catch (NoResultException $e) {
} catch (NoResultException) {
return null;
}
$codes = array('en'); // Semantics is 'en' by default.
Expand Down
2 changes: 1 addition & 1 deletion Entity/Library.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Library

#[ORM\OneToMany(targetEntity: ContentLibraries::class, mappedBy: "library")]
/**
* @var ArrayCollection|Collection
* @var ArrayCollection|Collection $contentLibraries
*/
private ArrayCollection|Collection $contentLibraries;
/**
Expand Down
23 changes: 14 additions & 9 deletions Entity/LibraryRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\NoResultException;
use Doctrine\Persistence\ManagerRegistry;
use Studit\H5PBundle\Service\DoctrineParser;

/**
* LibraryRepository
*
* This class was generated by the PhpStorm "Php Annotations" Plugin. Add your own custom
* repository methods below.
*/

class LibraryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
public function __construct(ManagerRegistry $registry, private readonly DoctrineParser $parser)
{
parent::__construct($registry, Library::class);
}
Expand Down Expand Up @@ -90,23 +90,25 @@ public function findLatestLibraryVersions(): array
}
return $libraryVersions;
}

public function findHasSemantics($machineName, $majorVersion, $minorVersion)
{
$qb = $this->createQueryBuilder('l')
->select('l')
->where('l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion and l.semantics is not null')
->setParameters([
->setParameters($this->parser->buildParams([
'machineName' => $machineName,
'majorVersion' => $majorVersion,
'minorVersion' => $minorVersion
]);
]));
try {
$library = $qb->getQuery()->getSingleResult();
} catch (NoResultException $e) {
return null;
}
return (object)$library;
}

public function findAllRunnableWithSemantics()
{
$qb = $this->createQueryBuilder('l')
Expand All @@ -119,40 +121,43 @@ public function findAllRunnableWithSemantics()
}
return $libraries;
}

public function findOneArrayBy($parameters)
{
$qb = $this->createQueryBuilder('l')
->where('l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion')
->setParameters($parameters);
->setParameters($this->parser->buildParams($parameters));
return $qb->getQuery()->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY);
}

public function findIdBy($machineName, $majorVersion, $minorVersion)
{
$qb = $this->createQueryBuilder('l')
->select('l.id')
->where('l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion and l.semantics is not null')
->setParameters([
->setParameters($this->parser->buildParams([
'machineName' => $machineName,
'majorVersion' => $majorVersion,
'minorVersion' => $minorVersion
]);
]));
try {
return $qb->getQuery()->getSingleScalarResult();
} catch (NoResultException $e) {
return null;
}
}

public function isPatched($library): bool
{
$qb = $this->createQueryBuilder('l')
->select('COUNT(l)')
->where('l.machineName = :machineName and l.majorVersion = :majorVersion and l.minorVersion = :minorVersion and l.patchVersion < :patchVersion')
->setParameters([
->setParameters($this->parser->buildParams([
'machineName' => $library['machineName'],
'majorVersion' => $library['majorVersion'],
'minorVersion' => $library['minorVersion'],
'patchVersion' => $library['patchVersion']
]);
]));
return $qb->getQuery()->getSingleScalarResult() > 0;
}
}
46 changes: 46 additions & 0 deletions Service/DoctrineParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Studit\H5PBundle\Service;

use Composer\InstalledVersions;
use Doctrine\Common\Collections\ArrayCollection;
use Studit\H5PBundle\Utils\VersionORM;

/**
* This class exists to prevent breaking changes when working with different versions of Doctrine ORM.
* For example, in Doctrine ORM v3.x, certain parameters require an ArrayCollection.
* @author Joris Dugué
*/
class DoctrineParser
{
private VersionORM $versionORM;

public function __construct(VersionORM $versionORM)
{
$this->versionORM = $versionORM;
}

/**
* This method converts parameters to an ArrayCollection for ORM v3.
* If using ORM v2, it simply returns the received parameters as is.
*
* @param array $params The input parameters to process.
* @return ArrayCollection|array Returns an ArrayCollection for ORM v3 or the original parameters for ORM v2.
*/
public function buildParams(array $params): ArrayCollection|array
{
$doctrineVersion = $this->versionORM->getDoctrineVersion();
if ($doctrineVersion !== null && str_starts_with($doctrineVersion, '3')) {
// For Doctrine ORM v3, ensure the parameters are returned as an ArrayCollection
$paramsCollection = [];

foreach ($params as $k => $val) {
$paramsCollection[] = new \Doctrine\ORM\Query\Parameter($k, $val);
}

return new ArrayCollection($paramsCollection);
}
// For Doctrine ORM v2, return the parameters as is
return $params;
}
}
78 changes: 78 additions & 0 deletions Tests/Service/DoctrineParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Studit\H5PBundle\Tests\Service;

use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\TestCase;
use Studit\H5PBundle\Service\DoctrineParser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Parameter;
use Studit\H5PBundle\Utils\VersionORM;

class DoctrineParserTest extends TestCase
{
/**
* Test that buildParams returns an ArrayCollection for Doctrine ORM v3.
* @throws Exception
*/
public function testBuildParamsForDoctrineV3()
{
// Mock VersionProvider to simulate Doctrine v2 version
$mockVersionORM = $this->createMock(VersionORM::class);
$mockVersionORM->method('getDoctrineVersion')
->willReturn('3.1.0'); // Simulate Doctrine v2 version
// Inject the mocked version provider into DoctrineParser
$doctrineParser = new DoctrineParser($mockVersionORM);

// Define test parameters
$params = [
'param1' => 'value1',
'param2' => 'value2',
];

// Call the method under test
$result = $doctrineParser->buildParams($params);

// Assert that the result is an instance of ArrayCollection
$this->assertInstanceOf(ArrayCollection::class, $result);

// Assert that the ArrayCollection contains Parameter objects
foreach ($result as $param) {
$this->assertInstanceOf(Parameter::class, $param);
}

// Assert that the parameters inside the Parameter objects match the input parameters
$this->assertEquals('value1', $result[0]->getValue());
$this->assertEquals('value2', $result[1]->getValue());
}

/**
* Test that buildParams returns the original parameters as an array for Doctrine ORM v2.
* @throws Exception
*/
public function testBuildParamsForDoctrineV2()
{
// Mock VersionProvider to simulate Doctrine v2 version
$mockVersionORM = $this->createMock(VersionORM::class);
$mockVersionORM->method('getDoctrineVersion')
->willReturn('2.9.3'); // Simulate Doctrine v2 version

// Inject the mocked version provider into DoctrineParser
$doctrineParser = new DoctrineParser($mockVersionORM);

// Define test parameters
$params = [
'param1' => 'value1',
'param2' => 'value2',
];

// Call the method under test
$result = $doctrineParser->buildParams($params);

// Assert that the result is an array (not an ArrayCollection)
$this->assertIsArray($result);

// Assert that the returned array contains the same values as the input parameters
$this->assertSame($params, $result);
}
}
25 changes: 25 additions & 0 deletions Utils/VersionORM.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Studit\H5PBundle\Utils;

use Composer\InstalledVersions;

/**
* VersionProvider class provides a simple interface to retrieve the version of the Doctrine ORM package.
* It wraps the `InstalledVersions::getVersion` method, allowing easier testing and version checking.
*/
class VersionORM
{
/**
* Retrieves the installed version of the Doctrine ORM package.
*
* This method calls `InstalledVersions::getVersion('doctrine/orm')` and returns the version string
* for the Doctrine ORM package if available. If the package is not found, it returns null.
*
* @return string|null The version of Doctrine ORM if installed, null otherwise.
*/
public function getDoctrineVersion(): ?string
{
return InstalledVersions::getVersion('doctrine/orm');
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jorisdugue/h5p-bundle",
"version": "3.0.1",
"version": "3.0.2",
"type": "symfony-bundle",
"description": "H5P Bundle for Symfony 5, 6 and Symfony 7",
"keywords": [
Expand Down Expand Up @@ -29,6 +29,7 @@
],
"require": {
"php": ">= 8.1",
"composer-runtime-api": "^2",
"doctrine/orm": "~2.0|~3.0",
"guzzlehttp/guzzle": "^7.9",
"h5p/h5p-core": "1.27",
Expand Down
Loading