1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Spiral\Database\Builders\Prototypes; |
10
|
|
|
|
11
|
|
|
use Spiral\Database\Builders\QueryBuilder; |
12
|
|
|
use Spiral\Database\Builders\Traits\JoinsTrait; |
13
|
|
|
use Spiral\Database\Entities\PDOResult; |
14
|
|
|
use Spiral\Database\Entities\QueryCompiler; |
15
|
|
|
use Spiral\Database\Exceptions\BuilderException; |
16
|
|
|
use Spiral\Database\Exceptions\QueryException; |
17
|
|
|
use Spiral\Database\Injections\ExpressionInterface; |
18
|
|
|
use Spiral\Database\Injections\FragmentInterface; |
19
|
|
|
use Spiral\Database\Injections\Parameter; |
20
|
|
|
use Spiral\Database\Injections\ParameterInterface; |
21
|
|
|
use Spiral\Pagination\PaginatorAwareInterface; |
22
|
|
|
use Spiral\Pagination\Traits\LimitsTrait; |
23
|
|
|
use Spiral\Pagination\Traits\PaginatorTrait; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Prototype for select queries, include ability to cache, paginate or chunk results. Support WHERE, |
27
|
|
|
* JOIN, HAVING, ORDER BY, GROUP BY, UNION and DISTINCT statements. In addition only desired set |
28
|
|
|
* of columns can be selected. In addition select. |
29
|
|
|
* |
30
|
|
|
* @see AbstractWhere |
31
|
|
|
* |
32
|
|
|
* @method int avg($identifier) Perform aggregation (AVG) based on column or expression value. |
33
|
|
|
* @method int min($identifier) Perform aggregation (MIN) based on column or expression value. |
34
|
|
|
* @method int max($identifier) Perform aggregation (MAX) based on column or expression value. |
35
|
|
|
* @method int sum($identifier) Perform aggregation (SUM) based on column or expression value. |
36
|
|
|
*/ |
37
|
|
|
abstract class AbstractSelect extends AbstractWhere implements |
38
|
|
|
\IteratorAggregate, |
39
|
|
|
PaginatorAwareInterface |
40
|
|
|
{ |
41
|
|
|
use JoinsTrait, LimitsTrait, PaginatorTrait; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Query type. |
45
|
|
|
*/ |
46
|
|
|
const QUERY_TYPE = QueryCompiler::SELECT_QUERY; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Sort directions. |
50
|
|
|
*/ |
51
|
|
|
const SORT_ASC = 'ASC'; |
52
|
|
|
const SORT_DESC = 'DESC'; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Query must return only unique rows. |
56
|
|
|
* |
57
|
|
|
* @var bool|string |
58
|
|
|
*/ |
59
|
|
|
protected $distinct = false; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Columns or expressions to be fetched from database, can include aliases (AS). |
63
|
|
|
* |
64
|
|
|
* @var array |
65
|
|
|
*/ |
66
|
|
|
protected $columns = ['*']; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Set of generated having tokens, format must be supported by QueryCompilers. |
70
|
|
|
* |
71
|
|
|
* @see AbstractWhere |
72
|
|
|
* |
73
|
|
|
* @var array |
74
|
|
|
*/ |
75
|
|
|
protected $havingTokens = []; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Parameters collected while generating HAVING tokens, must be in a same order as parameters |
79
|
|
|
* in resulted query. |
80
|
|
|
* |
81
|
|
|
* @see AbstractWhere |
82
|
|
|
* |
83
|
|
|
* @var array |
84
|
|
|
*/ |
85
|
|
|
protected $havingParameters = []; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Columns/expression associated with their sort direction (ASK|DESC). |
89
|
|
|
* |
90
|
|
|
* @var array |
91
|
|
|
*/ |
92
|
|
|
protected $ordering = []; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Columns/expressions to group by. |
96
|
|
|
* |
97
|
|
|
* @var array |
98
|
|
|
*/ |
99
|
|
|
protected $grouping = []; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* {@inheritdoc} |
103
|
|
|
*/ |
104
|
|
View Code Duplication |
public function getParameters(QueryCompiler $compiler = null): array |
|
|
|
|
105
|
|
|
{ |
106
|
|
|
$compiler = $compiler ?? $this->compiler; |
107
|
|
|
|
108
|
|
|
return $this->flattenParameters( |
109
|
|
|
$compiler->orderParameters( |
110
|
|
|
self::QUERY_TYPE, |
111
|
|
|
$this->whereParameters, |
112
|
|
|
$this->onParameters, |
113
|
|
|
$this->havingParameters |
114
|
|
|
) |
115
|
|
|
); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Mark query to return only distinct results. |
120
|
|
|
* |
121
|
|
|
* @param bool|string $distinct You are only allowed to use string value for Postgres databases. |
122
|
|
|
* |
123
|
|
|
* @return self|$this |
124
|
|
|
*/ |
125
|
|
|
public function distinct($distinct = true): AbstractSelect |
126
|
|
|
{ |
127
|
|
|
$this->distinct = $distinct; |
128
|
|
|
|
129
|
|
|
return $this; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Simple HAVING condition with various set of arguments. |
134
|
|
|
* |
135
|
|
|
* @see AbstractWhere |
136
|
|
|
* |
137
|
|
|
* @param string|mixed $identifier Column or expression. |
138
|
|
|
* @param mixed $variousA Operator or value. |
139
|
|
|
* @param mixed $variousB Value, if operator specified. |
140
|
|
|
* @param mixed $variousC Required only in between statements. |
141
|
|
|
* |
142
|
|
|
* @return self|$this |
143
|
|
|
* |
144
|
|
|
* @throws BuilderException |
145
|
|
|
*/ |
146
|
|
View Code Duplication |
public function having( |
|
|
|
|
147
|
|
|
$identifier, |
|
|
|
|
148
|
|
|
$variousA = null, |
|
|
|
|
149
|
|
|
$variousB = null, |
|
|
|
|
150
|
|
|
$variousC = null |
|
|
|
|
151
|
|
|
): AbstractSelect { |
152
|
|
|
$this->whereToken('AND', func_get_args(), $this->havingTokens, $this->havingWrapper()); |
153
|
|
|
|
154
|
|
|
return $this; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Simple AND HAVING condition with various set of arguments. |
159
|
|
|
* |
160
|
|
|
* @see AbstractWhere |
161
|
|
|
* |
162
|
|
|
* @param string|mixed $identifier Column or expression. |
163
|
|
|
* @param mixed $variousA Operator or value. |
164
|
|
|
* @param mixed $variousB Value, if operator specified. |
165
|
|
|
* @param mixed $variousC Required only in between statements. |
166
|
|
|
* |
167
|
|
|
* @return self|$this |
168
|
|
|
* |
169
|
|
|
* @throws BuilderException |
170
|
|
|
*/ |
171
|
|
View Code Duplication |
public function andHaving( |
|
|
|
|
172
|
|
|
$identifier, |
|
|
|
|
173
|
|
|
$variousA = null, |
|
|
|
|
174
|
|
|
$variousB = null, |
|
|
|
|
175
|
|
|
$variousC = null |
|
|
|
|
176
|
|
|
): AbstractSelect { |
177
|
|
|
$this->whereToken('AND', func_get_args(), $this->havingTokens, $this->havingWrapper()); |
178
|
|
|
|
179
|
|
|
return $this; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Simple OR HAVING condition with various set of arguments. |
184
|
|
|
* |
185
|
|
|
* @see AbstractWhere |
186
|
|
|
* |
187
|
|
|
* @param string|mixed $identifier Column or expression. |
188
|
|
|
* @param mixed $variousA Operator or value. |
189
|
|
|
* @param mixed $variousB Value, if operator specified. |
190
|
|
|
* @param mixed $variousC Required only in between statements. |
191
|
|
|
* |
192
|
|
|
* @return self|$this |
193
|
|
|
* |
194
|
|
|
* @throws BuilderException |
195
|
|
|
*/ |
196
|
|
View Code Duplication |
public function orHaving( |
|
|
|
|
197
|
|
|
$identifier, |
|
|
|
|
198
|
|
|
$variousA = [], |
|
|
|
|
199
|
|
|
$variousB = null, |
|
|
|
|
200
|
|
|
$variousC = null |
|
|
|
|
201
|
|
|
): AbstractSelect { |
202
|
|
|
$this->whereToken('OR', func_get_args(), $this->havingTokens, $this->havingWrapper()); |
203
|
|
|
|
204
|
|
|
return $this; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Sort result by column/expression. You can apply multiple sortings to query via calling method |
209
|
|
|
* few times or by specifying values using array of sort parameters:. |
210
|
|
|
* |
211
|
|
|
* $select->orderBy([ |
212
|
|
|
* 'id' => SelectQuery::SORT_DESC, |
213
|
|
|
* 'name' => SelectQuery::SORT_ASC |
214
|
|
|
* ]); |
215
|
|
|
* |
216
|
|
|
* @param string|array $expression |
217
|
|
|
* @param string $direction Sorting direction, ASC|DESC. |
218
|
|
|
* |
219
|
|
|
* @return self|$this |
220
|
|
|
*/ |
221
|
|
|
public function orderBy($expression, $direction = self::SORT_ASC): AbstractSelect |
222
|
|
|
{ |
223
|
|
|
if (!is_array($expression)) { |
224
|
|
|
$this->ordering[] = [$expression, $direction]; |
225
|
|
|
|
226
|
|
|
return $this; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
foreach ($expression as $nested => $direction) { |
230
|
|
|
$this->ordering[] = [$nested, $direction]; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return $this; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Column or expression to group query by. |
238
|
|
|
* |
239
|
|
|
* @param string $expression |
240
|
|
|
* |
241
|
|
|
* @return self|$this |
242
|
|
|
*/ |
243
|
|
|
public function groupBy($expression): AbstractSelect |
244
|
|
|
{ |
245
|
|
|
$this->grouping[] = $expression; |
246
|
|
|
|
247
|
|
|
return $this; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Applied to every potential parameter while having tokens generation. |
252
|
|
|
* |
253
|
|
|
* @return \Closure |
254
|
|
|
*/ |
255
|
|
View Code Duplication |
private function havingWrapper() |
|
|
|
|
256
|
|
|
{ |
257
|
|
|
return function ($parameter) { |
258
|
|
|
if ($parameter instanceof FragmentInterface) { |
259
|
|
|
|
260
|
|
|
//We are only not creating bindings for plan fragments |
261
|
|
|
if (!$parameter instanceof ParameterInterface && !$parameter instanceof QueryBuilder) { |
262
|
|
|
return $parameter; |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
if (is_array($parameter)) { |
267
|
|
|
throw new BuilderException('Arrays must be wrapped with Parameter instance'); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
//Wrapping all values with ParameterInterface |
271
|
|
|
if (!$parameter instanceof ParameterInterface && !$parameter instanceof ExpressionInterface) { |
272
|
|
|
$parameter = new Parameter($parameter, Parameter::DETECT_TYPE); |
273
|
|
|
}; |
274
|
|
|
|
275
|
|
|
//Let's store to sent to driver when needed |
276
|
|
|
$this->havingParameters[] = $parameter; |
277
|
|
|
|
278
|
|
|
return $parameter; |
279
|
|
|
}; |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
|
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.