1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Happyr Doctrine Specification package. |
5
|
|
|
* |
6
|
|
|
* (c) Tobias Nyholm <[email protected]> |
7
|
|
|
* Kacper Gunia <[email protected]> |
8
|
|
|
* Peter Gribanov <[email protected]> |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please view the LICENSE |
11
|
|
|
* file that was distributed with this source code. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace Happyr\DoctrineSpecification\Repository; |
15
|
|
|
|
16
|
|
|
use Doctrine\ORM\NonUniqueResultException as DoctrineNonUniqueResultException; |
17
|
|
|
use Doctrine\ORM\NoResultException as DoctrineNoResultException; |
18
|
|
|
use Doctrine\ORM\Query; |
19
|
|
|
use Doctrine\ORM\QueryBuilder; |
20
|
|
|
use Happyr\DoctrineSpecification\Exception\NonUniqueResultException; |
21
|
|
|
use Happyr\DoctrineSpecification\Exception\NoResultException; |
22
|
|
|
use Happyr\DoctrineSpecification\Filter\Filter; |
23
|
|
|
use Happyr\DoctrineSpecification\Query\QueryModifier; |
24
|
|
|
use Happyr\DoctrineSpecification\Result\ResultModifier; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* This trait should be used by a class extending \Doctrine\ORM\EntityRepository. |
28
|
|
|
*/ |
29
|
|
View Code Duplication |
trait EntitySpecificationRepositoryTrait |
|
|
|
|
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* @var string alias |
33
|
|
|
*/ |
34
|
|
|
private $alias = 'e'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Get results when you match with a Specification. |
38
|
|
|
* |
39
|
|
|
* @param Filter|QueryModifier $specification |
40
|
|
|
* @param ResultModifier|null $modifier |
41
|
|
|
* |
42
|
|
|
* @return mixed[] |
43
|
|
|
*/ |
44
|
|
|
public function match($specification, ResultModifier $modifier = null) |
45
|
|
|
{ |
46
|
|
|
$query = $this->getQuery($specification, $modifier); |
47
|
|
|
|
48
|
|
|
return $query->execute(); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Get single result when you match with a Specification. |
53
|
|
|
* |
54
|
|
|
* @param Filter|QueryModifier $specification |
55
|
|
|
* @param ResultModifier|null $modifier |
56
|
|
|
* |
57
|
|
|
* @throw Exception\NonUniqueException If more than one result is found |
58
|
|
|
* @throw Exception\NoResultException If no results found |
59
|
|
|
* |
60
|
|
|
* @return mixed |
61
|
|
|
*/ |
62
|
|
|
public function matchSingleResult($specification, ResultModifier $modifier = null) |
63
|
|
|
{ |
64
|
|
|
$query = $this->getQuery($specification, $modifier); |
65
|
|
|
|
66
|
|
|
try { |
67
|
|
|
return $query->getSingleResult(); |
68
|
|
|
} catch (DoctrineNonUniqueResultException $e) { |
69
|
|
|
throw new NonUniqueResultException($e->getMessage(), $e->getCode(), $e); |
70
|
|
|
} catch (DoctrineNoResultException $e) { |
71
|
|
|
throw new NoResultException($e->getMessage(), $e->getCode(), $e); |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Get single result or null when you match with a Specification. |
77
|
|
|
* |
78
|
|
|
* @param Filter|QueryModifier $specification |
79
|
|
|
* @param ResultModifier|null $modifier |
80
|
|
|
* |
81
|
|
|
* @throw Exception\NonUniqueException If more than one result is found |
82
|
|
|
* |
83
|
|
|
* @return mixed|null |
84
|
|
|
*/ |
85
|
|
|
public function matchOneOrNullResult($specification, ResultModifier $modifier = null) |
86
|
|
|
{ |
87
|
|
|
try { |
88
|
|
|
return $this->matchSingleResult($specification, $modifier); |
89
|
|
|
} catch (NoResultException $e) { |
90
|
|
|
return null; |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Get single scalar result when you match with a Specification. |
96
|
|
|
* |
97
|
|
|
* @param Filter|QueryModifier $specification |
98
|
|
|
* @param ResultModifier|null $modifier |
99
|
|
|
* |
100
|
|
|
* @throw Exception\NonUniqueException If more than one result is found |
101
|
|
|
* @throw Exception\NoResultException If no results found |
102
|
|
|
* |
103
|
|
|
* @return mixed |
104
|
|
|
*/ |
105
|
|
|
public function matchSingleScalarResult($specification, ResultModifier $modifier = null) |
106
|
|
|
{ |
107
|
|
|
$query = $this->getQuery($specification, $modifier); |
108
|
|
|
|
109
|
|
|
try { |
110
|
|
|
return $query->getSingleScalarResult(); |
111
|
|
|
} catch (DoctrineNonUniqueResultException $e) { |
112
|
|
|
throw new NonUniqueResultException($e->getMessage(), $e->getCode(), $e); |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Get scalar result when you match with a Specification. |
118
|
|
|
* |
119
|
|
|
* @param Filter|QueryModifier $specification |
120
|
|
|
* @param ResultModifier|null $modifier |
121
|
|
|
* |
122
|
|
|
* @throw Exception\NonUniqueException If more than one result is found |
123
|
|
|
* @throw Exception\NoResultException If no results found |
124
|
|
|
* |
125
|
|
|
* @return mixed |
126
|
|
|
*/ |
127
|
|
|
public function matchScalarResult($specification, ResultModifier $modifier = null) |
128
|
|
|
{ |
129
|
|
|
$query = $this->getQuery($specification, $modifier); |
130
|
|
|
|
131
|
|
|
return $query->getScalarResult(); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Prepare a Query with a Specification. |
136
|
|
|
* |
137
|
|
|
* @param Filter|QueryModifier $specification |
138
|
|
|
* @param ResultModifier|null $modifier |
139
|
|
|
* |
140
|
|
|
* @return Query |
141
|
|
|
*/ |
142
|
|
|
public function getQuery($specification, ResultModifier $modifier = null) |
143
|
|
|
{ |
144
|
|
|
$query = $this->getQueryBuilder($specification)->getQuery(); |
145
|
|
|
|
146
|
|
|
if (null !== $modifier) { |
147
|
|
|
$modifier->modify($query); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
return $query; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @param Filter|QueryModifier $specification |
155
|
|
|
* @param string|null $alias |
156
|
|
|
* |
157
|
|
|
* @return QueryBuilder |
158
|
|
|
*/ |
159
|
|
|
public function getQueryBuilder($specification, $alias = null) |
160
|
|
|
{ |
161
|
|
|
$qb = $this->createQueryBuilder($alias ?: $this->getAlias()); |
|
|
|
|
162
|
|
|
$this->applySpecification($qb, $specification, $alias); |
163
|
|
|
|
164
|
|
|
return $qb; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Iterate results when you match with a Specification. |
169
|
|
|
* |
170
|
|
|
* @param Filter|QueryModifier $specification |
171
|
|
|
* @param ResultModifier|null $modifier |
172
|
|
|
* |
173
|
|
|
* @return \Traversable |
174
|
|
|
*/ |
175
|
|
|
public function iterate($specification, ResultModifier $modifier = null) |
176
|
|
|
{ |
177
|
|
|
foreach ($this->getQuery($specification, $modifier)->iterate() as $row) { |
178
|
|
|
yield current($row); |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @param string $alias |
184
|
|
|
* |
185
|
|
|
* @return $this |
186
|
|
|
*/ |
187
|
|
|
public function setAlias($alias) |
188
|
|
|
{ |
189
|
|
|
$this->alias = $alias; |
190
|
|
|
|
191
|
|
|
return $this; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @return string |
196
|
|
|
*/ |
197
|
|
|
public function getAlias() |
198
|
|
|
{ |
199
|
|
|
return $this->alias; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @param QueryBuilder $queryBuilder |
204
|
|
|
* @param Filter|QueryModifier|mixed|null $specification |
205
|
|
|
* @param string $alias |
206
|
|
|
* |
207
|
|
|
* @throws \InvalidArgumentException |
208
|
|
|
*/ |
209
|
|
|
protected function applySpecification(QueryBuilder $queryBuilder, $specification = null, $alias = null) |
210
|
|
|
{ |
211
|
|
|
if (null === $specification) { |
212
|
|
|
return; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if (!$specification instanceof QueryModifier && !$specification instanceof Filter) { |
216
|
|
|
throw new \InvalidArgumentException(sprintf( |
217
|
|
|
'Expected argument of type "%s" or "%s", "%s" given.', |
218
|
|
|
QueryModifier::class, |
219
|
|
|
Filter::class, |
220
|
|
|
is_object($specification) ? get_class($specification) : gettype($specification) |
221
|
|
|
)); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
if ($specification instanceof QueryModifier) { |
225
|
|
|
$specification->modify($queryBuilder, $alias ?: $this->getAlias()); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
if ($specification instanceof Filter && |
229
|
|
|
($filter = $specification->getFilter($queryBuilder, $alias ?: $this->getAlias())) && |
230
|
|
|
($filter = trim($filter)) |
231
|
|
|
) { |
232
|
|
|
$queryBuilder->andWhere($filter); |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.