Completed
Push — master ( a0071b...e33605 )
by Michael
12s
created

lib/Doctrine/ORM/EntityRepository.php (1 issue)

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
13
/**
14
 * An EntityRepository serves as a repository for entities with generic as well as
15
 * business specific methods for retrieving entities.
16
 *
17
 * This class is designed for inheritance and users can subclass this class to
18
 * write their own repositories with business-specific methods to locate entities.
19
 */
20
class EntityRepository implements ObjectRepository, Selectable
21
{
22
    /**
23
     * @var string
24
     */
25
    protected $entityName;
26
27
    /**
28
     * @var EntityManagerInterface
29
     */
30
    protected $em;
31
32
    /**
33
     * @var \Doctrine\ORM\Mapping\ClassMetadata
34
     */
35
    protected $class;
36
37
    /**
38
     * Initializes a new <tt>EntityRepository</tt>.
39
     *
40
     * @param EntityManagerInterface $em    The EntityManager to use.
41
     * @param Mapping\ClassMetadata  $class The class descriptor.
42
     */
43 153
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
44
    {
45 153
        $this->entityName = $class->getClassName();
46 153
        $this->em         = $em;
47 153
        $this->class      = $class;
48 153
    }
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
     * Creates a new Query instance based on a predefined metadata named query.
84
     *
85
     * @param string $queryName
86
     *
87
     * @return Query
88
     */
89 4
    public function createNamedQuery($queryName)
90
    {
91 4
        $namedQuery    = $this->class->getNamedQuery($queryName);
92 3
        $resolvedQuery = str_replace('__CLASS__', $this->class->getClassName(), $namedQuery);
93
94 3
        return $this->em->createQuery($resolvedQuery);
95
    }
96
97
    /**
98
     * Creates a native SQL query.
99
     *
100
     * @param string $queryName
101
     *
102
     * @return NativeQuery
103
     */
104 9
    public function createNativeNamedQuery($queryName)
105
    {
106 9
        $queryMapping = $this->class->getNamedNativeQuery($queryName);
107 9
        $rsm          = new Query\ResultSetMappingBuilder($this->em);
108
109 9
        $rsm->addNamedNativeQueryMapping($this->class, $queryMapping);
110
111 9
        return $this->em->createNativeQuery($queryMapping['query'], $rsm);
112
    }
113
114
    /**
115
     * Clears the repository, causing all managed entities to become detached.
116
     */
117
    public function clear()
118
    {
119
        $this->em->clear($this->class->getRootClassName());
120
    }
121
122
    /**
123
     * Finds an entity by its primary key / identifier.
124
     *
125
     * @param mixed    $id          The identifier.
126
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
127
     *                              or NULL if no specific lock mode should be used
128
     *                              during the search.
129
     * @param int|null $lockVersion The lock version.
130
     *
131
     * @return object|null The entity instance or NULL if the entity can not be found.
132
     */
133 15
    public function find($id, $lockMode = null, $lockVersion = null)
134
    {
135 15
        return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
136
    }
137
138
    /**
139
     * Finds all entities in the repository.
140
     *
141
     * @return object[] The entities.
142
     */
143 34
    public function findAll()
144
    {
145 34
        return $this->findBy([]);
146
    }
147
148
    /**
149
     * Finds entities by a set of criteria.
150
     *
151
     * @param mixed[]  $criteria
152
     * @param mixed[]  $orderBy
153
     * @param int|null $limit
154
     * @param int|null $offset
155
     *
156
     * @return object[] The objects.
157
     *
158
     * @todo guilhermeblanco Change orderBy to use a blank array by default (requires Common\Persistence change).
159
     */
160 65
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
161
    {
162 65
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
163
164 65
        return $persister->loadAll($criteria, $orderBy !== null ? $orderBy : [], $limit, $offset);
165
    }
166
167
    /**
168
     * Finds a single entity by a set of criteria.
169
     *
170
     * @param mixed[] $criteria
171
     * @param mixed[] $orderBy
172
     *
173
     * @return object|null The entity instance or NULL if the entity can not be found.
174
     */
175 21
    public function findOneBy(array $criteria, array $orderBy = [])
176
    {
177 21
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
178
179 21
        return $persister->load($criteria, null, null, [], null, 1, $orderBy);
180
    }
181
182
    /**
183
     * Counts entities by a set of criteria.
184
     *
185
     * @todo Add this method to `ObjectRepository` interface in the next major release
186
     *
187
     * @param Criteria[] $criteria
188
     *
189
     * @return int The cardinality of the objects that match the given criteria.
190
     */
191 2
    public function count(array $criteria)
192
    {
193 2
        return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);
194
    }
195
196
    /**
197
     * Adds support for magic method calls.
198
     *
199
     * @param string  $method
200
     * @param mixed[] $arguments
201
     *
202
     * @return mixed The returned value from the resolved method.
203
     *
204
     * @throws ORMException
205
     * @throws \BadMethodCallException If the method called is invalid.
206
     */
207 14
    public function __call($method, $arguments)
208
    {
209 14
        if (strpos($method, 'findBy') === 0) {
210 8
            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
211
        }
212
213 6
        if (strpos($method, 'findOneBy') === 0) {
214 4
            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
215
        }
216
217 2
        if (strpos($method, 'countBy') === 0) {
218 1
            return $this->resolveMagicCall('count', substr($method, 7), $arguments);
219
        }
220
221 1
        throw new \BadMethodCallException(
222 1
            sprintf(
223 1
                "Undefined method '%s'. The method name must start with either findBy, findOneBy or countBy!",
224 1
                $method
225
            )
226
        );
227
    }
228
229
    /**
230
     * @return string
231
     */
232
    protected function getEntityName()
233
    {
234
        return $this->entityName;
235
    }
236
237
    /**
238
     * @return string
239
     */
240
    public function getClassName()
241
    {
242
        return $this->getEntityName();
243
    }
244
245
    /**
246
     * @return EntityManagerInterface
247
     */
248
    protected function getEntityManager()
249
    {
250
        return $this->em;
251
    }
252
253
    /**
254
     * @return Mapping\ClassMetadata
255
     */
256
    protected function getClassMetadata()
257
    {
258
        return $this->class;
259
    }
260
261
    /**
262
     * Select all elements from a selectable that match the expression and
263
     * return a new collection containing these elements.
264
     *
265
     * @return \Doctrine\Common\Collections\Collection|object[]
266
     */
267 26
    public function matching(Criteria $criteria)
268
    {
269 26
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
270
271 26
        return new LazyCriteriaCollection($persister, $criteria);
272
    }
273
274
    /**
275
     * Resolves a magic method call to the proper existent method at `EntityRepository`.
276
     *
277
     * @param string  $method    The method to call
278
     * @param string  $by        The property name used as condition
279
     * @param mixed[] $arguments The arguments to pass at method call
280
     *
281
     * @throws ORMException If the method called is invalid or the requested field/association does not exist.
282
     *
283
     * @return mixed
284
     */
285 13
    private function resolveMagicCall($method, $by, array $arguments)
286
    {
287 13
        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...
288 1
            throw ORMException::findByRequiresParameter($method . $by);
289
        }
290
291 12
        $fieldName = lcfirst(Inflector::classify($by));
292
293 12
        if ($this->class->getProperty($fieldName) === null) {
294 1
            throw ORMException::invalidMagicCall($this->entityName, $fieldName, $method . $by);
295
        }
296
297 11
        return $this->{$method}([$fieldName => $arguments[0]], ...array_slice($arguments, 1));
298
    }
299
}
300