Passed
Pull Request — 2.8.x (#7928)
by Oliver
08:12
created

EntityRepository   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Test Coverage

Coverage 87.5%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 24
eloc 47
c 2
b 0
f 1
dl 0
loc 275
ccs 56
cts 64
cp 0.875
rs 10

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getEntityName() 0 3 1
A getClassName() 0 3 1
A createNamedQuery() 0 3 1
A createNativeNamedQuery() 0 7 1
A getClassMetadata() 0 3 1
A getEntityManager() 0 3 1
A __construct() 0 5 1
A resolveMagicCall() 0 13 4
A matching() 0 5 1
A findOneBy() 0 5 1
A createQueryBuilder() 0 5 1
A find() 0 3 1
A findBy() 0 5 1
A findAll() 0 3 1
A count() 0 3 1
A createResultSetMappingBuilder() 0 6 1
A __call() 0 17 4
A clear() 0 5 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\Common\Inflector\Inflector;
23
use Doctrine\ORM\Query\ResultSetMappingBuilder;
24
use Doctrine\Common\Persistence\ObjectRepository;
25
use Doctrine\Common\Collections\Selectable;
26
use Doctrine\Common\Collections\Criteria;
27
use const E_USER_DEPRECATED;
28
use function trigger_error;
29
30
/**
31
 * An EntityRepository serves as a repository for entities with generic as well as
32
 * business specific methods for retrieving entities.
33
 *
34
 * This class is designed for inheritance and users can subclass this class to
35
 * write their own repositories with business-specific methods to locate entities.
36
 *
37
 * @since   2.0
38
 * @author  Benjamin Eberlei <[email protected]>
39
 * @author  Guilherme Blanco <[email protected]>
40
 * @author  Jonathan Wage <[email protected]>
41
 * @author  Roman Borschel <[email protected]>
42
 */
43
class EntityRepository implements ObjectRepository, Selectable
44
{
45
    /**
46
     * @var string
47
     */
48
    protected $_entityName;
49
50
    /**
51
     * @var EntityManager
52
     */
53
    protected $_em;
54
55
    /**
56
     * @var \Doctrine\ORM\Mapping\ClassMetadata
57
     */
58
    protected $_class;
59
60
    /**
61
     * Initializes a new <tt>EntityRepository</tt>.
62
     */
63 163
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
64
    {
65 163
        $this->_entityName = $class->name;
66 163
        $this->_em         = $em;
0 ignored issues
show
Documentation Bug introduced by
$em is of type Doctrine\ORM\EntityManagerInterface, but the property $_em was declared to be of type Doctrine\ORM\EntityManager. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
67 163
        $this->_class      = $class;
68 163
    }
69
70
    /**
71
     * Creates a new QueryBuilder instance that is prepopulated for this entity name.
72
     *
73
     * @param string $alias
74
     * @param string $indexBy The index for the from.
75
     *
76
     * @return QueryBuilder
77
     */
78 13
    public function createQueryBuilder($alias, $indexBy = null)
79
    {
80 13
        return $this->_em->createQueryBuilder()
81 13
            ->select($alias)
82 13
            ->from($this->_entityName, $alias, $indexBy);
83
    }
84
85
    /**
86
     * Creates a new result set mapping builder for this entity.
87
     *
88
     * The column naming strategy is "INCREMENT".
89
     *
90
     * @param string $alias
91
     *
92
     * @return ResultSetMappingBuilder
93
     */
94 1
    public function createResultSetMappingBuilder($alias)
95
    {
96 1
        $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
97 1
        $rsm->addRootEntityFromClassMetadata($this->_entityName, $alias);
98
99 1
        return $rsm;
100
    }
101
102
    /**
103
     * Creates a new Query instance based on a predefined metadata named query.
104
     *
105
     * @param string $queryName
106
     *
107
     * @return Query
108
     */
109 3
    public function createNamedQuery($queryName)
110
    {
111 3
        return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
112
    }
113
114
    /**
115
     * Creates a native SQL query.
116
     *
117
     * @param string $queryName
118
     *
119
     * @return NativeQuery
120
     */
121 7
    public function createNativeNamedQuery($queryName)
122
    {
123 7
        $queryMapping   = $this->_class->getNamedNativeQuery($queryName);
124 7
        $rsm            = new Query\ResultSetMappingBuilder($this->_em);
125 7
        $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping);
126
127 7
        return $this->_em->createNativeQuery($queryMapping['query'], $rsm);
128
    }
129
130
    /**
131
     * Clears the repository, causing all managed entities to become detached.
132
     *
133
     * @return void
134
     *
135
     * @deprecated 2.8 This method is being removed from the ORM and won't have any replacement
136
     */
137 1
    public function clear()
138
    {
139 1
        @trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
140
141 1
        $this->_em->clear($this->_class->rootEntityName);
142 1
    }
143
144
    /**
145
     * Finds an entity by its primary key / identifier.
146
     *
147
     * @param mixed    $id          The identifier.
148
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
149
     *                              or NULL if no specific lock mode should be used
150
     *                              during the search.
151
     * @param int|null $lockVersion The lock version.
152
     *
153
     * @return object|null The entity instance or NULL if the entity can not be found.
154
     */
155 15
    public function find($id, $lockMode = null, $lockVersion = null)
156
    {
157 15
        return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion);
158
    }
159
160
    /**
161
     * Finds all entities in the repository.
162
     *
163
     * @return array The entities.
164
     */
165 34
    public function findAll()
166
    {
167 34
        return $this->findBy([]);
168
    }
169
170
    /**
171
     * Finds entities by a set of criteria.
172
     *
173
     * @param array      $criteria
174
     * @param array|null $orderBy
175
     * @param int|null   $limit
176
     * @param int|null   $offset
177
     *
178
     * @return array The objects.
179
     */
180 65
    public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
181
    {
182 65
        $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
183
184 65
        return $persister->loadAll($criteria, $orderBy, $limit, $offset);
185
    }
186
187
    /**
188
     * Finds a single entity by a set of criteria.
189
     *
190
     * @param array      $criteria
191
     * @param array|null $orderBy
192
     *
193
     * @return object|null The entity instance or NULL if the entity can not be found.
194
     */
195 22
    public function findOneBy(array $criteria, array $orderBy = null)
196
    {
197 22
        $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
198
199 22
        return $persister->load($criteria, null, null, [], null, 1, $orderBy);
200
    }
201
202
    /**
203
     * Counts entities by a set of criteria.
204
     *
205
     * @todo Add this method to `ObjectRepository` interface in the next major release
206
     *
207
     * @param array $criteria
208
     *
209
     * @return int The cardinality of the objects that match the given criteria.
210
     */
211 2
    public function count(array $criteria)
212
    {
213 2
        return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->count($criteria);
214
    }
215
216
    /**
217
     * Adds support for magic method calls.
218
     *
219
     * @param string $method
220
     * @param array  $arguments
221
     *
222
     * @return mixed The returned value from the resolved method.
223
     *
224
     * @throws ORMException
225
     * @throws \BadMethodCallException If the method called is invalid
226
     */
227 14
    public function __call($method, $arguments)
228
    {
229 14
        if (0 === strpos($method, 'findBy')) {
230 8
            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
231
        }
232
233 6
        if (0 === strpos($method, 'findOneBy')) {
234 4
            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
235
        }
236
237 2
        if (0 === strpos($method, 'countBy')) {
238 1
            return $this->resolveMagicCall('count', substr($method, 7), $arguments);
239
        }
240
241 1
        throw new \BadMethodCallException(
242 1
            "Undefined method '$method'. The method name must start with ".
243 1
            "either findBy, findOneBy or countBy!"
244
        );
245
    }
246
247
    /**
248
     * @return string
249
     */
250
    protected function getEntityName()
251
    {
252
        return $this->_entityName;
253
    }
254
255
    /**
256
     * @return string
257
     */
258
    public function getClassName()
259
    {
260
        return $this->getEntityName();
261
    }
262
263
    /**
264
     * @return EntityManager
265
     */
266
    protected function getEntityManager()
267
    {
268
        return $this->_em;
269
    }
270
271
    /**
272
     * @return Mapping\ClassMetadata
273
     */
274
    protected function getClassMetadata()
275
    {
276
        return $this->_class;
277
    }
278
279
    /**
280
     * Select all elements from a selectable that match the expression and
281
     * return a new collection containing these elements.
282
     *
283
     * @param \Doctrine\Common\Collections\Criteria $criteria
284
     *
285
     * @return \Doctrine\Common\Collections\Collection
286
     */
287 26
    public function matching(Criteria $criteria)
288
    {
289 26
        $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
290
291 26
        return new LazyCriteriaCollection($persister, $criteria);
292
    }
293
294
    /**
295
     * Resolves a magic method call to the proper existent method at `EntityRepository`.
296
     *
297
     * @param string $method    The method to call
298
     * @param string $by        The property name used as condition
299
     * @param array  $arguments The arguments to pass at method call
300
     *
301
     * @throws ORMException If the method called is invalid or the requested field/association does not exist
302
     *
303
     * @return mixed
304
     */
305 13
    private function resolveMagicCall($method, $by, array $arguments)
306
    {
307 13
        if (! $arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array 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...
308 1
            throw ORMException::findByRequiresParameter($method . $by);
309
        }
310
311 12
        $fieldName = lcfirst(Inflector::classify($by));
312
313 12
        if (! ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName))) {
314 1
            throw ORMException::invalidMagicCall($this->_entityName, $fieldName, $method . $by);
315
        }
316
317 11
        return $this->$method([$fieldName => $arguments[0]], ...array_slice($arguments, 1));
318
    }
319
}
320