Completed
Pull Request — master (#777)
by
unknown
02:27
created

ContainerRepositoryFactory::getRepository()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 40
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 40
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 8
nop 2
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\EntityRepository;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
use Doctrine\ORM\Repository\RepositoryFactory;
11
use Psr\Container\ContainerInterface;
12
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
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
0 ignored issues
show
Documentation introduced by
Should the type for parameter $container not be null|ContainerInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
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;
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
46
47
        $customRepositoryName = $metadata->customRepositoryClassName;
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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 EntityRepository) {
54
                    throw new \RuntimeException(sprintf('The service "%s" must extend EntityRepository (or a base class, like ServiceEntityRepository).', $repositoryServiceId));
55
                }
56
                if ($repository instanceof ServiceEntityRepositoryInterface) {
57
                    $repository->setEntityManager($entityManager);
58
                }
59
60
                return $repository;
61
            }
62
63
            // if not in the container but the class/id implements the interface, throw an error
64
            if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
65
                // can be removed when DoctrineBundle requires Symfony 3.3
66
                if ($this->container === null) {
67
                    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));
68
                }
69
70
                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));
71
            }
72
73
            if (! class_exists($customRepositoryName)) {
74
                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));
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
75
            }
76
77
            // allow the repository to be created below
78
        }
79
80
        return $this->getOrCreateRepository($entityManager, $metadata);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
81
    }
82
83
    private function getOrCreateRepository(EntityManagerInterface $entityManager, ClassMetadata $metadata)
84
    {
85
        $repositoryHash = $metadata->getName() . spl_object_hash($entityManager);
86
        if (isset($this->managedRepositories[$repositoryHash])) {
87
            return $this->managedRepositories[$repositoryHash];
88
        }
89
90
        $repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
91
92
        return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
93
    }
94
}
95