Failed Conditions
Pull Request — master (#6743)
by Grégoire
14:59
created

EntityRepository::getClassMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\Common\Collections\Selectable;
9
use Doctrine\Common\Persistence\ObjectRepository;
10
use Doctrine\Common\Util\Inflector;
11
use Doctrine\ORM\Query\ResultSetMappingBuilder;
12
use Doctrine\ORM\Repository\InvalidMagicMethodCall;
13
14
/**
15
 * An EntityRepository serves as a repository for entities with generic as well as
16
 * business specific methods for retrieving entities.
17
 *
18
 * This class is designed for inheritance and users can subclass this class to
19
 * write their own repositories with business-specific methods to locate entities.
20
 */
21
class EntityRepository implements ObjectRepository, Selectable
22
{
23
    /**
24
     * @var string
25
     */
26
    protected $entityName;
27
28
    /**
29
     * @var EntityManagerInterface
30
     */
31
    protected $em;
32
33
    /**
34
     * @var \Doctrine\ORM\Mapping\ClassMetadata
35
     */
36
    protected $class;
37
38
    /**
39
     * Initializes a new <tt>EntityRepository</tt>.
40
     *
41
     * @param EntityManagerInterface $em    The EntityManager to use.
42
     * @param Mapping\ClassMetadata  $class The class descriptor.
43
     */
44 146
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
45
    {
46 146
        $this->entityName = $class->getClassName();
47 146
        $this->em         = $em;
48 146
        $this->class      = $class;
49 146
    }
50
51
    /**
52
     * Creates a new QueryBuilder instance that is prepopulated for this entity name.
53
     *
54
     * @param string $alias
55
     * @param string $indexBy The index for the from.
56
     *
57
     * @return QueryBuilder
58
     */
59 8
    public function createQueryBuilder($alias, $indexBy = null)
60
    {
61 8
        return $this->em->createQueryBuilder()
62 8
            ->select($alias)
63 8
            ->from($this->entityName, $alias, $indexBy);
64
    }
65
66
    /**
67
     * Creates a new result set mapping builder for this entity.
68
     *
69
     * The column naming strategy is "INCREMENT".
70
     *
71
     * @param string $alias
72
     *
73
     * @return ResultSetMappingBuilder
74
     */
75 1
    public function createResultSetMappingBuilder($alias)
76
    {
77 1
        $rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
78 1
        $rsm->addRootEntityFromClassMetadata($this->entityName, $alias);
79
80 1
        return $rsm;
81
    }
82
83
    /**
84
     * Creates a native SQL query.
85
     *
86
     * @param string $queryName
87
     *
88
     * @return NativeQuery
89
     */
90 9
    public function createNativeNamedQuery($queryName)
91
    {
92 9
        $queryMapping = $this->class->getNamedNativeQuery($queryName);
93 9
        $rsm          = new Query\ResultSetMappingBuilder($this->em);
94
95 9
        $rsm->addNamedNativeQueryMapping($this->class, $queryMapping);
96
97 9
        return $this->em->createNativeQuery($queryMapping['query'], $rsm);
98
    }
99
100
    /**
101
     * Clears the repository, causing all managed entities to become detached.
102
     */
103
    public function clear()
104
    {
105
        $this->em->clear($this->class->getRootClassName());
106
    }
107
108
    /**
109
     * Finds an entity by its primary key / identifier.
110
     *
111
     * @param mixed    $id          The identifier.
112
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
113
     *                              or NULL if no specific lock mode should be used
114
     *                              during the search.
115
     * @param int|null $lockVersion The lock version.
116
     *
117
     * @return object|null The entity instance or NULL if the entity can not be found.
118
     */
119 15
    public function find($id, $lockMode = null, $lockVersion = null)
120
    {
121 15
        return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\Common\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

121
        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...
122
    }
123
124
    /**
125
     * Finds all entities in the repository.
126
     *
127
     * @return object[] The entities.
128
     */
129 33
    public function findAll()
130
    {
131 33
        return $this->findBy([]);
132
    }
133
134
    /**
135
     * Finds entities by a set of criteria.
136
     *
137
     * @param mixed[]  $criteria
138
     * @param mixed[]  $orderBy
139
     * @param int|null $limit
140
     * @param int|null $offset
141
     *
142
     * @return object[] The objects.
143
     *
144
     * @todo guilhermeblanco Change orderBy to use a blank array by default (requires Common\Persistence change).
145
     */
146 64
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
147
    {
148 64
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
149
150 64
        return $persister->loadAll($criteria, $orderBy !== null ? $orderBy : [], $limit, $offset);
151
    }
152
153
    /**
154
     * Finds a single entity by a set of criteria.
155
     *
156
     * @param mixed[] $criteria
157
     * @param mixed[] $orderBy
158
     *
159
     * @return object|null The entity instance or NULL if the entity can not be found.
160
     */
161 20
    public function findOneBy(array $criteria, array $orderBy = [])
162
    {
163 20
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
164
165 20
        return $persister->load($criteria, null, null, [], null, 1, $orderBy);
166
    }
167
168
    /**
169
     * Counts entities by a set of criteria.
170
     *
171
     * @todo Add this method to `ObjectRepository` interface in the next major release
172
     *
173
     * @param Criteria[] $criteria
174
     *
175
     * @return int The cardinality of the objects that match the given criteria.
176
     */
177 2
    public function count(array $criteria)
178
    {
179 2
        return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);
180
    }
181
182
    /**
183
     * Adds support for magic method calls.
184
     *
185
     * @param string  $method
186
     * @param mixed[] $arguments
187
     *
188
     * @return mixed The returned value from the resolved method.
189
     *
190
     * @throws ORMException
191
     * @throws \BadMethodCallException If the method called is invalid.
192
     */
193 13
    public function __call($method, $arguments)
194
    {
195 13
        if (strpos($method, 'findBy') === 0) {
196 8
            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
197
        }
198
199 5
        if (strpos($method, 'findOneBy') === 0) {
200 3
            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
201
        }
202
203 2
        if (strpos($method, 'countBy') === 0) {
204 1
            return $this->resolveMagicCall('count', substr($method, 7), $arguments);
205
        }
206
207 1
        throw new \BadMethodCallException(
208 1
            sprintf(
209 1
                "Undefined method '%s'. The method name must start with either findBy, findOneBy or countBy!",
210 1
                $method
211
            )
212
        );
213
    }
214
215
    /**
216
     * @return string
217
     */
218
    protected function getEntityName()
219
    {
220
        return $this->entityName;
221
    }
222
223
    /**
224
     * @return string
225
     */
226
    public function getClassName()
227
    {
228
        return $this->getEntityName();
229
    }
230
231
    /**
232
     * @return EntityManagerInterface
233
     */
234
    protected function getEntityManager()
235
    {
236
        return $this->em;
237
    }
238
239
    /**
240
     * @return Mapping\ClassMetadata
241
     */
242
    protected function getClassMetadata()
243
    {
244
        return $this->class;
245
    }
246
247
    /**
248
     * Select all elements from a selectable that match the expression and
249
     * return a new collection containing these elements.
250
     *
251
     * @return \Doctrine\Common\Collections\Collection|object[]
252
     */
253 26
    public function matching(Criteria $criteria)
254
    {
255 26
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
256
257 26
        return new LazyCriteriaCollection($persister, $criteria);
258
    }
259
260
    /**
261
     * Resolves a magic method call to the proper existent method at `EntityRepository`.
262
     *
263
     * @param string  $method    The method to call
264
     * @param string  $by        The property name used as condition
265
     * @param mixed[] $arguments The arguments to pass at method call
266
     *
267
     * @throws ORMException If the method called is invalid or the requested field/association does not exist.
268
     *
269
     * @return mixed
270
     */
271 12
    private function resolveMagicCall($method, $by, array $arguments)
272
    {
273 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...
274 1
            throw InvalidMagicMethodCall::onMissingParameter($method . $by);
275
        }
276
277 11
        $fieldName = lcfirst(Inflector::classify($by));
278
279 11
        if ($this->class->getProperty($fieldName) === null) {
280 1
            throw InvalidMagicMethodCall::becauseFieldNotFoundIn(
281 1
                $this->entityName,
282 1
                $fieldName,
283 1
                $method . $by
284
            );
285
        }
286
287 10
        return $this->{$method}([$fieldName => $arguments[0]], ...array_slice($arguments, 1));
288
    }
289
}
290