Skip to content

Commit

Permalink
Allow service repositories to be used with more than one manager
Browse files Browse the repository at this point in the history
  • Loading branch information
ipernet committed Nov 4, 2019
1 parent 2d64753 commit 6179e2a
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
5 changes: 5 additions & 0 deletions Repository/ContainerRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public function getRepository(EntityManagerInterface $entityManager, $entityName
throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).', $repositoryServiceId));
}

// Use the correct manager when supported
if (method_exists($repository, 'withManager')) {
return $repository->withManager($entityManager);
}

return $repository;
}

Expand Down
90 changes: 86 additions & 4 deletions Repository/ServiceEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Doctrine\Bundle\DoctrineBundle\Repository;

use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use LogicException;

Expand All @@ -14,20 +16,52 @@
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(RegistryInterface $registry)
* public function __construct(RegistryInterface $registry, ...$args)
* {
* parent::__construct($registry, YourEntity::class);
* parent::__construct($registry, YourEntity::class, func_get_args());
* }
* }
*/
class ServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
/**
* All the arguments used by the constructor of the user-defined service repository
*
* @var mixed[]
*/
private $args;

/** @var Registry */
private $registry;

/** @var EntityManager */
private $manager;

/** @var string[] */
private static $nonDefaultUserRepositoriesIds = [];

/**
* @param string $entityClass The class name of the entity this repository manages
*/
public function __construct(ManagerRegistry $registry, $entityClass)
public function __construct(ManagerRegistry $registry, string $entityClass, ...$args)
{
$manager = $registry->getManagerForClass($entityClass);
$userSpecifiedManager = null;

// BC
if (isset($args[0])) {
$this->args = $args[0];

// Is there a specific entity manager to use here? To be searched in the extra args we internally manage.
foreach ($this->args as $arg) {
if ($arg instanceof EntityManager) {
$userSpecifiedManager = $arg;
break;
}
}
}

// Default manager: "first one defined for the entity"
$manager = $userSpecifiedManager ? : $registry->getManagerForClass($entityClass);

if ($manager === null) {
throw new LogicException(sprintf(
Expand All @@ -36,6 +70,54 @@ public function __construct(ManagerRegistry $registry, $entityClass)
));
}

$this->registry = $registry;
$this->manager = $manager;

parent::__construct($manager, $manager->getClassMetadata($entityClass));
}

/**
* @param string|EntityManager $entityManagerRef
*
* @return self
*/
public function withManager($entityManagerRef)
{
if (! $this->supportsMultipleManagers()) {
// BC: Fails silently with the old behavior
return $this;
}

// getFrom: instance
if ($entityManagerRef instanceof EntityManager) {
$userSpecifiedManager = $entityManagerRef;
} else { // getFrom: name
$userSpecifiedManager = $this->registry->getManager($entityManagerRef);
}

// If a different manager than the autowired-one is required, instantiate a new user's service-repository with the right one.
if ($userSpecifiedManager !== $this->manager) {
$managerInstanceId = static::class . ':' . spl_object_hash($userSpecifiedManager);

if (! isset(self::$nonDefaultUserRepositoriesIds[$managerInstanceId])) {
// Use the very-same arguments for the new instance but the manager name is added to be used as the default manager.
$args = $this->args;
$args[] = $userSpecifiedManager;

self::$nonDefaultUserRepositoriesIds[$managerInstanceId] = new static(...$args);
}

return self::$nonDefaultUserRepositoriesIds[$managerInstanceId];
}

return $this;
}

/**
* @return bool
*/
public function supportsMultipleManagers(): bool
{
return $this->args !== null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

class TestCustomServiceRepoRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
public function __construct(RegistryInterface $registry, ...$args)
{
parent::__construct($registry, TestCustomServiceRepoEntity::class);
parent::__construct($registry, TestCustomServiceRepoEntity::class, func_get_args());
}
}
2 changes: 1 addition & 1 deletion Tests/Repository/ServiceEntityRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ class ServiceEntityRepositoryTest extends TestCase
public function testConstructorThrowsExceptionWhenNoManagerFound()
{
$registry = $this->getMockBuilder(ManagerRegistry::class)->getMock();
new ServiceEntityRepository($registry, TestEntity::class);
new ServiceEntityRepository($registry, TestEntity::class, [[]]);
}
}

0 comments on commit 6179e2a

Please sign in to comment.