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

EntityRepository::createQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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

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