1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Kdyby (http://www.kdyby.org) |
5
|
|
|
* |
6
|
|
|
* Copyright (c) 2008 Filip Procházka ([email protected]) |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the file license.txt that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Kdyby\Doctrine; |
12
|
|
|
|
13
|
|
|
use Doctrine; |
14
|
|
|
use Doctrine\ORM\AbstractQuery; |
15
|
|
|
use Doctrine\ORM\NoResultException; |
16
|
|
|
use Doctrine\ORM\NonUniqueResultException; |
17
|
|
|
use Kdyby; |
18
|
|
|
use Kdyby\Persistence; |
19
|
|
|
use Nette; |
20
|
|
|
|
21
|
|
|
|
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* This class is an extension to EntityRepository and should help you with prototyping. |
25
|
|
|
* The first and only rule with EntityRepository is not to ever inherit them, ever. |
26
|
|
|
* |
27
|
|
|
* The only valid reason to inherit EntityRepository is to add more common methods to all EntityRepositories in application, |
28
|
|
|
* when you're creating your own framework (but do we really need to go any deeper than this?). |
29
|
|
|
* |
30
|
|
|
* @author Filip Procházka <[email protected]> |
31
|
|
|
*/ |
32
|
|
|
class EntityRepository extends Doctrine\ORM\EntityRepository implements Persistence\QueryExecutor, Persistence\Queryable //, Persistence\ObjectFactory |
33
|
|
|
{ |
34
|
|
|
|
35
|
|
|
use \Kdyby\StrictObjects\Scream; |
36
|
|
|
|
37
|
|
|
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
38
|
|
|
{ |
39
|
|
|
if ($this->criteriaRequiresDql($criteria) === FALSE && $this->criteriaRequiresDql((array) $orderBy) === FALSE) { |
40
|
|
|
return parent::findBy($criteria, $orderBy, $limit, $offset); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
$qb = $this->createQueryBuilder('e') |
44
|
|
|
->whereCriteria($criteria) |
45
|
|
|
->autoJoinOrderBy((array) $orderBy); |
46
|
|
|
|
47
|
|
|
return $qb->getQuery() |
48
|
|
|
->setMaxResults($limit) |
49
|
|
|
->setFirstResult($offset) |
50
|
|
|
->getResult(); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
|
54
|
|
|
|
55
|
|
|
public function findOneBy(array $criteria, array $orderBy = null) |
56
|
|
|
{ |
57
|
|
|
if ($this->criteriaRequiresDql($criteria) === FALSE && $this->criteriaRequiresDql((array) $orderBy) === FALSE) { |
58
|
|
|
return parent::findOneBy($criteria, $orderBy); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
$qb = $this->createQueryBuilder('e') |
62
|
|
|
->whereCriteria($criteria) |
63
|
|
|
->autoJoinOrderBy((array) $orderBy); |
64
|
|
|
|
65
|
|
|
try { |
66
|
|
|
return $qb->setMaxResults(1) |
67
|
|
|
->getQuery()->getSingleResult(); |
68
|
|
|
|
69
|
|
|
} catch (NoResultException $e) { |
70
|
|
|
return NULL; |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
|
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @param array $criteria |
78
|
|
|
* @return int |
79
|
|
|
*/ |
80
|
|
|
public function countBy(array $criteria = []) |
81
|
|
|
{ |
82
|
|
|
return (int) $this->createQueryBuilder('e') |
83
|
|
|
->whereCriteria($criteria) |
84
|
|
|
->select('COUNT(e)') |
85
|
|
|
->getQuery()->getSingleScalarResult(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @param array $criteria |
92
|
|
|
* @return bool |
93
|
|
|
*/ |
94
|
|
|
private function criteriaRequiresDql(array $criteria) |
95
|
|
|
{ |
96
|
|
|
foreach ($criteria as $key => $val) { |
97
|
|
|
if (preg_match('~[\\?\\s\\.]~', $key)) { |
98
|
|
|
return TRUE; |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return FALSE; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Fetches all records like $key => $value pairs |
109
|
|
|
* |
110
|
|
|
* @param array $criteria parameter can be skipped |
111
|
|
|
* @param string $value mandatory |
112
|
|
|
* @param array|string $orderBy parameter can be skipped |
113
|
|
|
* @param string $key optional |
114
|
|
|
* |
115
|
|
|
* @throws QueryException |
116
|
|
|
* @return array |
117
|
|
|
*/ |
118
|
|
|
public function findPairs($criteria, $value = NULL, $orderBy = [], $key = NULL) |
119
|
|
|
{ |
120
|
|
|
if (!is_array($criteria)) { |
121
|
|
|
$key = $orderBy; |
122
|
|
|
$orderBy = $value; |
123
|
|
|
$value = $criteria; |
124
|
|
|
$criteria = []; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if (!is_array($orderBy)) { |
128
|
|
|
$key = $orderBy; |
129
|
|
|
$orderBy = []; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if (empty($key)) { |
133
|
|
|
$key = $this->getClassMetadata()->getSingleIdentifierFieldName(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** @var \Kdyby\Doctrine\QueryBuilder $qb */ |
137
|
|
|
$qb = $this->createQueryBuilder('e') |
138
|
|
|
->whereCriteria($criteria) |
139
|
|
|
->select(["e.$value", "e.$key"]) |
140
|
|
|
->resetDQLPart('from')->from($this->getEntityName(), 'e', 'e.' . $key); |
141
|
|
|
$query = $qb->autoJoinOrderBy($orderBy)->getQuery(); |
142
|
|
|
|
143
|
|
|
try { |
144
|
|
|
return array_map(function ($row) { |
145
|
|
|
return reset($row); |
146
|
|
|
}, $query->getResult(AbstractQuery::HYDRATE_ARRAY)); |
147
|
|
|
|
148
|
|
|
} catch (\Exception $e) { |
149
|
|
|
throw $this->handleException($e, $query); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
|
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Fetches all records and returns an associative array indexed by key |
157
|
|
|
* |
158
|
|
|
* @param array $criteria |
159
|
|
|
* @param string $key |
160
|
|
|
* |
161
|
|
|
* @throws \Exception|QueryException |
162
|
|
|
* @return array |
163
|
|
|
*/ |
164
|
|
|
public function findAssoc($criteria, $key = NULL) |
165
|
|
|
{ |
166
|
|
|
if (!is_array($criteria)) { |
167
|
|
|
$key = $criteria; |
168
|
|
|
$criteria = []; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$query = $this->createQueryBuilder('e') |
172
|
|
|
->whereCriteria($criteria) |
173
|
|
|
->resetDQLPart('from')->from($this->getEntityName(), 'e', 'e.' . $key) |
174
|
|
|
->getQuery(); |
175
|
|
|
|
176
|
|
|
try { |
177
|
|
|
return $query->getResult(); |
178
|
|
|
|
179
|
|
|
} catch (\Exception $e) { |
180
|
|
|
throw $this->handleException($e, $query); |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
|
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @param string $sql |
188
|
|
|
* @param Doctrine\ORM\Query\ResultSetMapping $rsm |
189
|
|
|
* @return Doctrine\ORM\NativeQuery |
190
|
|
|
*/ |
191
|
|
|
public function createNativeQuery($sql, Doctrine\ORM\Query\ResultSetMapping $rsm) |
192
|
|
|
{ |
193
|
|
|
return $this->getEntityManager()->createNativeQuery($sql, $rsm); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
|
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* @param string $alias |
200
|
|
|
* @param string $indexBy The index for the from. |
201
|
|
|
* @return \Kdyby\Doctrine\QueryBuilder |
202
|
|
|
*/ |
203
|
|
|
public function createQueryBuilder($alias = NULL, $indexBy = NULL) |
204
|
|
|
{ |
205
|
|
|
$qb = $this->getEntityManager()->createQueryBuilder(); |
206
|
|
|
|
207
|
|
|
if ($alias !== NULL) { |
208
|
|
|
$qb->select($alias)->from($this->getEntityName(), $alias, $indexBy); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
return $qb; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
|
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param string $dql |
218
|
|
|
* |
219
|
|
|
* @return \Doctrine\ORM\Query |
220
|
|
|
*/ |
221
|
|
|
public function createQuery($dql = NULL) |
222
|
|
|
{ |
223
|
|
|
$dql = implode(' ', func_get_args()); |
224
|
|
|
|
225
|
|
|
return $this->getEntityManager()->createQuery($dql); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
|
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param \Kdyby\Persistence\Query|\Kdyby\Doctrine\QueryObject $queryObject |
232
|
|
|
* @param int $hydrationMode |
233
|
|
|
* @throws QueryException |
234
|
|
|
* @return array|\Kdyby\Doctrine\ResultSet |
235
|
|
|
*/ |
236
|
|
|
public function fetch(Persistence\Query $queryObject, $hydrationMode = AbstractQuery::HYDRATE_OBJECT) |
237
|
|
|
{ |
238
|
|
|
try { |
239
|
|
|
return $queryObject->fetch($this, $hydrationMode); |
|
|
|
|
240
|
|
|
|
241
|
|
|
} catch (\Exception $e) { |
242
|
|
|
throw $this->handleQueryException($e, $queryObject); |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
|
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param \Kdyby\Persistence\Query|\Kdyby\Doctrine\QueryObject $queryObject |
250
|
|
|
* |
251
|
|
|
* @throws InvalidStateException |
252
|
|
|
* @throws QueryException |
253
|
|
|
* @return object |
254
|
|
|
*/ |
255
|
|
|
public function fetchOne(Persistence\Query $queryObject) |
256
|
|
|
{ |
257
|
|
|
try { |
258
|
|
|
return $queryObject->fetchOne($this); |
259
|
|
|
|
260
|
|
|
} catch (NoResultException $e) { |
261
|
|
|
return NULL; |
262
|
|
|
|
263
|
|
|
} catch (NonUniqueResultException $e) { // this should never happen! |
264
|
|
|
throw new InvalidStateException("You have to setup your query calling ->setMaxResult(1).", 0, $e); |
265
|
|
|
|
266
|
|
|
} catch (\Exception $e) { |
267
|
|
|
throw $this->handleQueryException($e, $queryObject); |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
|
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* @param integer|array $id |
275
|
|
|
* @return \Doctrine\ORM\Proxy\Proxy |
276
|
|
|
*/ |
277
|
|
|
public function getReference($id) |
278
|
|
|
{ |
279
|
|
|
return $this->getEntityManager()->getReference($this->_entityName, $id); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
|
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* @param \Exception $e |
286
|
|
|
* @param \Kdyby\Persistence\Query $queryObject |
287
|
|
|
* @return \Kdyby\Doctrine\QueryException |
288
|
|
|
*/ |
289
|
|
|
private function handleQueryException(\Exception $e, Persistence\Query $queryObject) |
290
|
|
|
{ |
291
|
|
|
$lastQuery = $queryObject instanceof QueryObject ? $queryObject->getLastQuery() : NULL; |
292
|
|
|
|
293
|
|
|
return new QueryException($e, $lastQuery, '[' . get_class($queryObject) . '] ' . $e->getMessage()); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
|
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* @param \Exception $e |
300
|
|
|
* @param \Doctrine\ORM\Query $query |
301
|
|
|
* @param string $message |
302
|
|
|
* @return \Exception|\Kdyby\Doctrine\QueryException |
303
|
|
|
*/ |
304
|
|
|
private function handleException(\Exception $e, Doctrine\ORM\Query $query = NULL, $message = NULL) |
305
|
|
|
{ |
306
|
|
|
if ($e instanceof Doctrine\ORM\Query\QueryException) { |
307
|
|
|
return new QueryException($e, $query, $message); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
return $e; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
|
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* @return \Kdyby\Doctrine\Mapping\ClassMetadata |
317
|
|
|
*/ |
318
|
|
|
public function getClassMetadata() |
319
|
|
|
{ |
320
|
|
|
/** @var \Kdyby\Doctrine\Mapping\ClassMetadata $classMetadata */ |
321
|
|
|
$classMetadata = parent::getClassMetadata(); |
322
|
|
|
return $classMetadata; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
|
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* @return \Kdyby\Doctrine\EntityManager |
329
|
|
|
*/ |
330
|
|
|
public function getEntityManager() |
331
|
|
|
{ |
332
|
|
|
/** @var \Kdyby\Doctrine\EntityManager $entityManager */ |
333
|
|
|
$entityManager = parent::getEntityManager(); |
334
|
|
|
return $entityManager; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
|
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* @param string $relation |
341
|
|
|
* @return \Kdyby\Doctrine\EntityRepository |
342
|
|
|
*/ |
343
|
|
|
public function related($relation) |
344
|
|
|
{ |
345
|
|
|
$meta = $this->getClassMetadata(); |
346
|
|
|
$targetClass = $meta->getAssociationTargetClass($relation); |
347
|
|
|
|
348
|
|
|
/** @var \Kdyby\Doctrine\EntityRepository $entityRepository */ |
349
|
|
|
$entityRepository = $this->getEntityManager()->getRepository($targetClass); |
350
|
|
|
return $entityRepository; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
} |
354
|
|
|
|
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.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.