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

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

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