EntityRepository   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Test Coverage

Coverage 81.36%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 45
c 2
b 0
f 0
dl 0
loc 241
ccs 48
cts 59
cp 0.8136
rs 10
wmc 21

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getEntityName() 0 3 1
A getClassName() 0 3 1
A getClassMetadata() 0 3 1
A getEntityManager() 0 3 1
A clear() 0 3 1
A createQueryBuilder() 0 5 1
A find() 0 3 1
A findAll() 0 3 1
A count() 0 3 1
A createResultSetMappingBuilder() 0 6 1
A __construct() 0 5 1
A matching() 0 5 1
A findOneBy() 0 5 1
A findBy() 0 5 1
A __call() 0 18 4
A resolveMagicCall() 0 17 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use BadMethodCallException;
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\Common\Collections\Criteria;
10
use Doctrine\Common\Collections\Selectable;
11
use Doctrine\Common\Inflector\Inflector;
12
use Doctrine\Common\Persistence\ObjectRepository;
13
use Doctrine\ORM\Mapping\ClassMetadata;
14
use Doctrine\ORM\Query\ResultSetMappingBuilder;
15
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
16
use function array_slice;
17
use function lcfirst;
18
use function sprintf;
19
use function strpos;
20
use function substr;
21
22
/**
23
 * An EntityRepository serves as a repository for entities with generic as well as
24
 * business specific methods for retrieving entities.
25
 *
26
 * This class is designed for inheritance and users can subclass this class to
27
 * write their own repositories with business-specific methods to locate entities.
28
 */
29
class EntityRepository implements ObjectRepository, Selectable
30
{
31
    /** @var string */
32
    protected $entityName;
33
34
    /** @var EntityManagerInterface */
35
    protected $em;
36
37
    /** @var ClassMetadata */
38
    protected $class;
39
40
    /**
41
     * Initializes a new <tt>EntityRepository</tt>.
42
     */
43 138
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
44
    {
45 138
        $this->entityName = $class->getClassName();
46 138
        $this->em         = $em;
47 138
        $this->class      = $class;
48 138
    }
49
50
    /**
51
     * Creates a new QueryBuilder instance that is prepopulated for this entity name.
52
     *
53
     * @param string $alias
54
     * @param string $indexBy The index for the from.
55
     *
56
     * @return QueryBuilder
57
     */
58 8
    public function createQueryBuilder($alias, $indexBy = null)
59
    {
60 8
        return $this->em->createQueryBuilder()
61 8
            ->select($alias)
62 8
            ->from($this->entityName, $alias, $indexBy);
63
    }
64
65
    /**
66
     * Creates a new result set mapping builder for this entity.
67
     *
68
     * The column naming strategy is "INCREMENT".
69
     *
70
     * @param string $alias
71
     *
72
     * @return ResultSetMappingBuilder
73
     */
74 1
    public function createResultSetMappingBuilder($alias)
75
    {
76 1
        $rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
77 1
        $rsm->addRootEntityFromClassMetadata($this->entityName, $alias);
78
79 1
        return $rsm;
80
    }
81
82
    /**
83
     * Clears the repository, causing all managed entities to become detached.
84
     */
85
    public function clear()
86
    {
87
        $this->em->clear($this->class->getRootClassName());
88
    }
89
90
    /**
91
     * Finds an entity by its primary key / identifier.
92
     *
93
     * @param mixed    $id          The identifier.
94
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
95
     *                              or NULL if no specific lock mode should be used
96
     *                              during the search.
97
     * @param int|null $lockVersion The lock version.
98
     *
99
     * @return object|null The entity instance or NULL if the entity can not be found.
100
     */
101 16
    public function find($id, $lockMode = null, $lockVersion = null)
102
    {
103 16
        return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\Persistence\ObjectManager::find() has too many arguments starting with $lockMode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

103
        return $this->em->/** @scrutinizer ignore-call */ find($this->entityName, $id, $lockMode, $lockVersion);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
104
    }
105
106
    /**
107
     * Finds all entities in the repository.
108
     *
109
     * @return object[] The entities.
110
     */
111 33
    public function findAll()
112
    {
113 33
        return $this->findBy([]);
114
    }
115
116
    /**
117
     * Finds entities by a set of criteria.
118
     *
119
     * @param mixed[]  $criteria
120
     * @param mixed[]  $orderBy
121
     * @param int|null $limit
122
     * @param int|null $offset
123
     *
124
     * @return object[] The objects.
125
     *
126
     * @todo guilhermeblanco Change orderBy to use a blank array by default (requires Common\Persistence change).
127
     */
128 64
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
129
    {
130 64
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
131
132 64
        return $persister->loadAll($criteria, $orderBy ?? [], $limit, $offset);
133
    }
134
135
    /**
136
     * Finds a single entity by a set of criteria.
137
     *
138
     * @param mixed[] $criteria
139
     * @param mixed[] $orderBy
140
     *
141
     * @return object|null The entity instance or NULL if the entity can not be found.
142
     */
143 20
    public function findOneBy(array $criteria, array $orderBy = [])
144
    {
145 20
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
146
147 20
        return $persister->load($criteria, null, null, [], null, 1, $orderBy);
148
    }
149
150
    /**
151
     * Counts entities by a set of criteria.
152
     *
153
     * @param Criteria[] $criteria
154
     *
155
     * @return int The cardinality of the objects that match the given criteria.
156
     *
157
     * @todo Add this method to `ObjectRepository` interface in the next major release
158
     */
159 2
    public function count(array $criteria)
160
    {
161 2
        return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);
162
    }
163
164
    /**
165
     * Adds support for magic method calls.
166
     *
167
     * @param string  $method
168
     * @param mixed[] $arguments
169
     *
170
     * @return mixed The returned value from the resolved method.
171
     *
172
     * @throws ORMException
173
     * @throws BadMethodCallException If the method called is invalid.
174
     */
175 13
    public function __call($method, $arguments)
176
    {
177 13
        if (strpos($method, 'findBy') === 0) {
178 8
            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
179
        }
180
181 5
        if (strpos($method, 'findOneBy') === 0) {
182 3
            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
183
        }
184
185 2
        if (strpos($method, 'countBy') === 0) {
186 1
            return $this->resolveMagicCall('count', substr($method, 7), $arguments);
187
        }
188
189 1
        throw new BadMethodCallException(
190 1
            sprintf(
191 1
                "Undefined method '%s'. The method name must start with either findBy, findOneBy or countBy!",
192 1
                $method
193
            )
194
        );
195
    }
196
197
    /**
198
     * @return string
199
     */
200
    protected function getEntityName()
201
    {
202
        return $this->entityName;
203
    }
204
205
    /**
206
     * @return string
207
     */
208
    public function getClassName()
209
    {
210
        return $this->getEntityName();
211
    }
212
213
    /**
214
     * @return EntityManagerInterface
215
     */
216
    protected function getEntityManager()
217
    {
218
        return $this->em;
219
    }
220
221
    /**
222
     * @return Mapping\ClassMetadata
223
     */
224
    protected function getClassMetadata()
225
    {
226
        return $this->class;
227
    }
228
229
    /**
230
     * Select all elements from a selectable that match the expression and
231
     * return a new collection containing these elements.
232
     *
233
     * @return Collection|object[]
234
     */
235 26
    public function matching(Criteria $criteria)
236
    {
237 26
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
238
239 26
        return new LazyCriteriaCollection($persister, $criteria);
240
    }
241
242
    /**
243
     * Resolves a magic method call to the proper existent method at `EntityRepository`.
244
     *
245
     * @param string  $method    The method to call
246
     * @param string  $by        The property name used as condition
247
     * @param mixed[] $arguments The arguments to pass at method call
248
     *
249
     * @return mixed
250
     *
251
     * @throws ORMException If the method called is invalid or the requested field/association does not exist.
252
     */
253 12
    private function resolveMagicCall($method, $by, array $arguments)
254
    {
255 12
        if (! $arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array<mixed,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
256 1
            throw InvalidMagicMethodCall::onMissingParameter($method . $by);
257
        }
258
259 11
        $fieldName = lcfirst(Inflector::classify($by));
260
261 11
        if ($this->class->getProperty($fieldName) === null) {
262 1
            throw InvalidMagicMethodCall::becauseFieldNotFoundIn(
263 1
                $this->entityName,
264
                $fieldName,
265 1
                $method . $by
266
            );
267
        }
268
269 10
        return $this->{$method}([$fieldName => $arguments[0]], ...array_slice($arguments, 1));
270
    }
271
}
272