diff --git a/Repository/ContainerRepositoryFactory.php b/Repository/ContainerRepositoryFactory.php index ae668e097..87544095e 100644 --- a/Repository/ContainerRepositoryFactory.php +++ b/Repository/ContainerRepositoryFactory.php @@ -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; } diff --git a/Repository/ServiceEntityRepository.php b/Repository/ServiceEntityRepository.php index 639634f28..e8cf2b521 100644 --- a/Repository/ServiceEntityRepository.php +++ b/Repository/ServiceEntityRepository.php @@ -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; @@ -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( @@ -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; + } } diff --git a/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoRepository.php b/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoRepository.php index 644017c69..20a5cd930 100644 --- a/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoRepository.php +++ b/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoRepository.php @@ -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()); } } diff --git a/Tests/Repository/ServiceEntityRepositoryTest.php b/Tests/Repository/ServiceEntityRepositoryTest.php index 499c99b44..93e4a95f7 100644 --- a/Tests/Repository/ServiceEntityRepositoryTest.php +++ b/Tests/Repository/ServiceEntityRepositoryTest.php @@ -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, [[]]); } }