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
|
|||
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 |
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.