Completed
Pull Request — master (#857)
by Maxime
02:51
created

ContainerRepositoryFactory   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 75
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 4
dl 0
loc 75
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\Repository;
4
5
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
6
use Doctrine\Common\Persistence\ObjectRepository;
7
use Doctrine\ORM\EntityManagerInterface;
8
use Doctrine\ORM\Mapping\ClassMetadata;
9
use Doctrine\ORM\Repository\RepositoryFactory;
10
use Psr\Container\ContainerInterface;
11
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
12
use Doctrine\Common\Persistence\ObjectRepository;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Cannot use Doctrine\Common\Persistence\ObjectRepository as ObjectRepository because the name is already in use
Loading history...
13
14
/**
15
 * Fetches repositories from the container or falls back to normal creation.
16
 */
17
final class ContainerRepositoryFactory implements RepositoryFactory
18
{
19
    /** @var ObjectRepository[] */
20
    private $managedRepositories = [];
21
22
    /** @var ContainerInterface|null */
23
    private $container;
24
25
    /**
26
     * @param ContainerInterface $container A service locator containing the repositories
27
     */
28
    public function __construct(ContainerInterface $container = null)
29
    {
30
        // When DoctrineBundle requires Symfony 3.3+, this can be removed
31
        // and the $container argument can become required.
32
        if ($container === null && class_exists(ServiceLocatorTagPass::class)) {
33
            throw new \InvalidArgumentException(sprintf('The first argument of %s::__construct() is required on Symfony 3.3 or higher.', self::class));
34
        }
35
36
        $this->container = $container;
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function getRepository(EntityManagerInterface $entityManager, $entityName)
43
    {
44
        $metadata            = $entityManager->getClassMetadata($entityName);
45
        $repositoryServiceId = $metadata->customRepositoryClassName;
46
47
        $customRepositoryName = $metadata->customRepositoryClassName;
48
        if ($customRepositoryName !== null) {
49
            // fetch from the container
50
            if ($this->container && $this->container->has($customRepositoryName)) {
51
                $repository = $this->container->get($customRepositoryName);
52
53
                if (! $repository instanceof ObjectRepository) {
54
                    throw new \RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).', $repositoryServiceId));
55
                }
56
57
                return $repository;
58
            }
59
60
            // if not in the container but the class/id implements the interface, throw an error
61
            if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
62
                // can be removed when DoctrineBundle requires Symfony 3.3
63
                if ($this->container === null) {
64
                    throw new \RuntimeException(sprintf('Support for loading entities from the service container only works for Symfony 3.3 or higher. Upgrade your version of Symfony or make sure your "%s" class does not implement "%s"', $customRepositoryName, ServiceEntityRepositoryInterface::class));
65
                }
66
67
                throw new \RuntimeException(sprintf('The "%s" entity repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".', $customRepositoryName, ServiceEntityRepositoryInterface::class, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
68
            }
69
70
            if (! class_exists($customRepositoryName)) {
71
                throw new \RuntimeException(sprintf('The "%s" entity has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".', $metadata->name, $customRepositoryName, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG));
72
            }
73
74
            // allow the repository to be created below
75
        }
76
77
        return $this->getOrCreateRepository($entityManager, $metadata);
78
    }
79
80
    private function getOrCreateRepository(EntityManagerInterface $entityManager, ClassMetadata $metadata)
81
    {
82
        $repositoryHash = $metadata->getName() . spl_object_hash($entityManager);
83
        if (isset($this->managedRepositories[$repositoryHash])) {
84
            return $this->managedRepositories[$repositoryHash];
85
        }
86
87
        $repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
88
89
        return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
90
    }
91
}
92