Failed Conditions
Push — master ( 6744b4...2b8acb )
by Marco
60:45 queued 60:36
created

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

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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