1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Class SelectAbstract |
4
|
|
|
* |
5
|
|
|
* @filesource SelectAbstract.php |
6
|
|
|
* @created 03.06.2017 |
7
|
|
|
* @package chillerlan\Database\Query\Dialects |
8
|
|
|
* @author Smiley <[email protected]> |
9
|
|
|
* @copyright 2017 Smiley |
10
|
|
|
* @license MIT |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace chillerlan\Database\Query; |
14
|
|
|
|
15
|
|
|
|
16
|
|
|
use chillerlan\Database\DBResult; |
17
|
|
|
use chillerlan\Database\Query\Traits\WhereTrait; |
18
|
|
|
|
19
|
|
|
abstract class SelectAbstract extends StatementAbstract implements SelectInterface{ |
20
|
|
|
use WhereTrait; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var bool |
24
|
|
|
*/ |
25
|
|
|
protected $distinct = false; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $cols = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var array |
34
|
|
|
*/ |
35
|
|
|
protected $from = []; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
protected $orderby = []; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array |
44
|
|
|
*/ |
45
|
|
|
protected $groupby = []; |
46
|
|
|
|
47
|
|
|
public function cached():SelectInterface{ |
48
|
|
|
$this->cached = true; |
49
|
|
|
|
50
|
|
|
return $this; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @param $val1 |
55
|
|
|
* @param $val2 |
56
|
|
|
* @param string $operator |
57
|
|
|
* @param bool $bind |
58
|
|
|
* @param string $join |
59
|
|
|
* |
60
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
61
|
|
|
*/ |
62
|
|
|
public function where($val1, $val2, $operator = '=', $bind = true, $join = 'AND'):SelectInterface{ |
63
|
|
|
return $this->_addWhere($val1, $val2, $operator, $bind, $join); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @param null $join |
68
|
|
|
* |
69
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
70
|
|
|
*/ |
71
|
|
|
public function openBracket($join = null):SelectInterface{ |
72
|
|
|
return $this->_openBracket($join); // @codeCoverageIgnore |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
77
|
|
|
*/ |
78
|
|
|
public function closeBracket():SelectInterface{ |
79
|
|
|
return $this->_closeBracket(); // @codeCoverageIgnore |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
84
|
|
|
*/ |
85
|
|
|
public function distinct():SelectInterface{ |
86
|
|
|
$this->distinct = true; |
87
|
|
|
|
88
|
|
|
return $this; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @param $expr1 |
93
|
|
|
* @param null $expr2 |
94
|
|
|
* @param null $func |
95
|
|
|
* |
96
|
|
|
* @return void |
97
|
|
|
*/ |
98
|
|
|
protected function addColumn($expr1, $expr2 = null, $func = null){ |
99
|
|
|
// @todo: quotes |
100
|
|
|
switch(true){ |
101
|
|
View Code Duplication |
case $expr2 && $func: |
|
|
|
|
102
|
|
|
$col = sprintf('%s(%s) AS %s', strtoupper($func), $this->quote($expr1), $this->quote($expr2)); break; |
|
|
|
|
103
|
|
View Code Duplication |
case $expr2 && !$func: |
|
|
|
|
104
|
|
|
$col = sprintf('%s AS %s', $this->quote($expr1), $this->quote($expr2)); break; |
|
|
|
|
105
|
|
View Code Duplication |
case !$expr2 && $func: |
|
|
|
|
106
|
|
|
$col = sprintf('%s(%s)', strtoupper($func), $this->quote($expr1)); break; |
|
|
|
|
107
|
|
|
case !$expr2 && !$func: |
108
|
|
|
default: |
109
|
|
|
$col = $this->quote($expr1); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
$this->cols[$expr2 ?? $expr1] = $col; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @param array $expressions |
117
|
|
|
* |
118
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
119
|
|
|
*/ |
120
|
|
|
public function cols(array $expressions):SelectInterface{ |
121
|
|
|
|
122
|
|
|
foreach($expressions as $k => $ref){ |
123
|
|
|
|
124
|
|
|
if(is_string($k)){ |
125
|
|
|
is_array($ref) |
126
|
|
|
? $this->addColumn($ref[0], $k, $ref[1] ?? null) |
127
|
|
|
: $this->addColumn($ref ,$k); |
128
|
|
|
} |
129
|
|
|
else{ |
130
|
|
|
is_array($ref) |
131
|
|
|
? $this->addColumn($ref[0], null, $ref[1] ?? null) |
132
|
|
|
: $this->addColumn($ref); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
return $this; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @param string $table |
142
|
|
|
* @param string|null $ref |
143
|
|
|
*/ |
144
|
|
|
protected function _addFrom(string $table, string $ref = null){ |
145
|
|
|
// @todo: quotes |
146
|
|
|
$from = $this->quote($table); |
147
|
|
|
|
148
|
|
|
if($ref){ |
|
|
|
|
149
|
|
|
$from = sprintf('%s AS %s', $this->quote($ref), $this->quote($table));// @todo: index hint |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
$this->from[$ref ?? $table] = $from; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* @param array $expressions |
157
|
|
|
* |
158
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
159
|
|
|
*/ |
160
|
|
|
public function from(array $expressions):SelectInterface{ |
161
|
|
|
|
162
|
|
|
foreach($expressions as $k => $ref){ |
163
|
|
|
|
164
|
|
|
if(is_string($k)){ |
165
|
|
|
$this->_addFrom($k, $ref); |
166
|
|
|
} |
167
|
|
|
else{ |
168
|
|
|
$x = explode(' ', $ref); |
169
|
|
|
|
170
|
|
|
if(count($x) === 2){ |
171
|
|
|
$this->_addFrom($x[0], $x[1]); |
172
|
|
|
} |
173
|
|
|
else{ |
174
|
|
|
$this->_addFrom($ref); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return $this; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @param int $limit |
185
|
|
|
* |
186
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
187
|
|
|
*/ |
188
|
|
|
public function limit(int $limit):SelectInterface{ |
189
|
|
|
$this->limit = $limit; |
190
|
|
|
|
191
|
|
|
return $this; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param int $offset |
196
|
|
|
* |
197
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
198
|
|
|
*/ |
199
|
|
|
public function offset(int $offset):SelectInterface{ |
200
|
|
|
$this->offset = $offset; |
201
|
|
|
|
202
|
|
|
return $this; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* @param array $expressions |
207
|
|
|
* |
208
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
209
|
|
|
*/ |
210
|
|
|
public function orderBy(array $expressions):SelectInterface{ |
211
|
|
|
|
212
|
|
|
foreach($expressions as $alias => $expression){ |
213
|
|
|
|
214
|
|
|
if(is_string($alias)){ |
215
|
|
|
|
216
|
|
|
if(is_array($expression)){ |
217
|
|
|
$dir = strtoupper($expression[0]); |
218
|
|
|
|
219
|
|
|
if(in_array($dir, ['ASC', 'DESC'])){ |
220
|
|
|
$this->orderby[] = isset($expression[1]) ? strtoupper($expression[1]).'('.$this->quote($alias).') '.$dir : $dir; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
} |
224
|
|
|
else{ |
225
|
|
|
$dir = strtoupper($expression); |
226
|
|
|
|
227
|
|
|
if(in_array($dir, ['ASC', 'DESC'])){ |
228
|
|
|
$this->orderby[] = $this->quote($alias).' '.$dir; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
} |
234
|
|
|
else{ |
235
|
|
|
$this->orderby[] = $this->quote($expression); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
return $this; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* @param array $expressions |
245
|
|
|
* |
246
|
|
|
* @return \chillerlan\Database\Query\SelectInterface |
247
|
|
|
*/ |
248
|
|
|
public function groupBy(array $expressions):SelectInterface{ |
249
|
|
|
|
250
|
|
|
foreach($expressions as $expression){ |
251
|
|
|
$this->groupby[] = $this->quote($expression); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
return $this; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @todo |
259
|
|
|
* |
260
|
|
|
* @return int |
261
|
|
|
* @throws \chillerlan\Database\Query\QueryException |
262
|
|
|
*/ |
263
|
|
|
public function count():int{ |
264
|
|
|
|
265
|
|
|
if(empty($this->from)){ |
266
|
|
|
throw new QueryException('no FROM expression specified'); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$glue = ','.PHP_EOL."\t"; |
270
|
|
|
|
271
|
|
|
$sql = 'SELECT '; |
272
|
|
|
$sql .= $this->distinct ? 'DISTINCT ' : ''; |
273
|
|
|
$sql .= 'COUNT(*) AS '.$this->quote('count'); |
274
|
|
|
$sql .= 'FROM '.implode($glue , $this->from); |
275
|
|
|
$sql .= $this->_getWhere(); |
276
|
|
|
$sql .= !empty($this->groupby) ? PHP_EOL.'GROUP BY '.implode($glue, $this->groupby) : ''; |
277
|
|
|
|
278
|
|
|
$q = $this->DBDriver->prepared($sql, $this->bindValues); |
279
|
|
|
|
280
|
|
|
if($q instanceof DBResult && $q->length > 0){ |
281
|
|
|
return (int)$q[0]->count; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
return -1; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
} |
288
|
|
|
|
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.