1
|
|
|
<?php |
|
|
|
|
2
|
|
|
namespace PhpBoot\DB\rules\select; |
3
|
|
|
use PhpBoot\DB\Context; |
4
|
|
|
use PhpBoot\DB\rules\basic\BasicRule; |
5
|
|
|
use PhpBoot\DB\impls\ExecImpl; |
6
|
|
|
use PhpBoot\DB\impls\SelectImpl; |
7
|
|
|
use PhpBoot\DB\impls\FromImpl; |
8
|
|
|
use PhpBoot\DB\impls\JoinImpl; |
9
|
|
|
use PhpBoot\DB\impls\JoinOnImpl; |
10
|
|
|
use PhpBoot\DB\impls\WhereImpl; |
11
|
|
|
use PhpBoot\DB\impls\GroupByImpl; |
12
|
|
|
use PhpBoot\DB\impls\OrderByImpl; |
13
|
|
|
use PhpBoot\DB\impls\LimitImpl; |
14
|
|
|
use PhpBoot\DB\impls\ForUpdateOfImpl; |
15
|
|
|
use PhpBoot\DB\impls\ForUpdateImpl; |
16
|
|
|
use PhpBoot\DB\rules\basic\ScopedQuery; |
17
|
|
|
|
18
|
|
|
require_once dirname(__DIR__).'/impls.php'; |
19
|
|
|
require_once __DIR__.'/basic.php'; |
20
|
|
|
|
21
|
|
|
class SelectRule extends BasicRule |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* select('column0, column1') => "SELECT column0, column1" |
25
|
|
|
* select('column0', 'column1') => "SELECT column0, column1" |
26
|
|
|
* @param string $columns |
27
|
|
|
* @return \PhpBoot\DB\rules\select\FromRule |
28
|
|
|
*/ |
29
|
28 |
|
public function select($columns) { |
30
|
28 |
|
SelectImpl::select($this->context, $columns); |
31
|
28 |
|
return new FromRule($this->context); |
32
|
|
|
} |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
class GetRule extends BasicRule |
|
|
|
|
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* Execute sql and get responses |
39
|
|
|
* @param string|false $asDict |
40
|
|
|
* @return array |
41
|
|
|
*/ |
42
|
27 |
|
public function get($asDict=false) { |
43
|
27 |
|
return ExecImpl::get($this->context, $asDict); |
|
|
|
|
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @return int|false |
48
|
|
|
*/ |
49
|
|
|
public function count() { |
50
|
|
|
return ExecImpl::count($this->context); |
51
|
|
|
} |
52
|
|
|
/** |
53
|
|
|
* Execute sql and get one response |
54
|
|
|
* @return null |
55
|
|
|
*/ |
56
|
1 |
|
public function getFirst(){ |
57
|
1 |
|
$res = ExecImpl::get($this->context); |
58
|
1 |
|
if(count($res)){ |
59
|
1 |
|
return $res[0]; |
60
|
|
|
} |
61
|
|
|
return null; |
62
|
|
|
} |
63
|
|
|
} |
64
|
|
|
class FromRule extends GetRule |
|
|
|
|
65
|
|
|
{ |
66
|
|
|
/** |
67
|
|
|
* from('table') => "FROM table" |
68
|
|
|
* @param string $table |
69
|
|
|
* @return \PhpBoot\DB\rules\select\JoinRule |
70
|
|
|
*/ |
71
|
27 |
|
public function from($table, $as=null){ |
72
|
27 |
|
FromImpl::from($this->context, $table,$as); |
73
|
27 |
|
return new JoinRule($this->context); |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
class ForUpdateOfRule extends GetRule |
|
|
|
|
77
|
|
|
{ |
78
|
|
|
/** |
79
|
|
|
* forUpdate()->of('column') => 'FOR UPDATE OF column' |
80
|
|
|
* @param string $column |
81
|
|
|
* @return \PhpBoot\DB\rules\select\GetRule |
82
|
|
|
*/ |
83
|
1 |
|
public function of($column){ |
84
|
1 |
|
ForUpdateOfImpl::of($this->context, $column); |
85
|
1 |
|
return new GetRule($this->context); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
class ForUpdateRule extends GetRule |
|
|
|
|
89
|
|
|
{ |
90
|
|
|
/** |
91
|
|
|
* forUpdate() => 'FOR UPDATE' |
92
|
|
|
* @return \PhpBoot\DB\rules\select\ForUpdateOfRule |
93
|
|
|
*/ |
94
|
2 |
|
public function forUpdate(){ |
95
|
2 |
|
ForUpdateImpl::forUpdate($this->context); |
96
|
2 |
|
return new ForUpdateOfRule($this->context); |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
class LimitRule extends ForUpdateRule |
|
|
|
|
101
|
|
|
{ |
102
|
|
|
/** |
103
|
|
|
* limit(0,1) => "LIMIT 0,1" |
104
|
|
|
* @param int $start |
105
|
|
|
* @param int $size |
106
|
|
|
* @return \PhpBoot\DB\rules\select\ForUpdateRule |
107
|
|
|
*/ |
108
|
1 |
|
public function limit($start, $size) { |
109
|
1 |
|
LimitImpl::limitWithOffset($this->context, $start, $size); |
110
|
1 |
|
return new ForUpdateRule($this->context); |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
|
View Code Duplication |
class OrderByRule extends LimitRule |
|
|
|
|
115
|
|
|
{ |
116
|
27 |
|
public function __construct($context){ |
117
|
27 |
|
parent::__construct($context); |
118
|
27 |
|
$this->order = new OrderByImpl(); |
119
|
27 |
|
} |
120
|
|
|
/** |
121
|
|
|
* orderBy('column') => "ORDER BY column" |
122
|
|
|
* orderBy('column', Sql::ORDER_BY_ASC) => "ORDER BY column ASC" |
123
|
|
|
* orderBy('column0')->orderBy('column1') => "ORDER BY column0, column1" |
124
|
|
|
* |
125
|
|
|
* orderBy(['column0', 'column1'=>Sql::ORDER_BY_ASC]) => "ORDER BY column0,column1 ASC" |
126
|
|
|
* |
127
|
|
|
* @param string $column |
128
|
|
|
* @param string $order Sql::ORDER_BY_ASC or Sql::ORDER_BY_DESC |
129
|
|
|
* @return \PhpBoot\DB\rules\select\OrderByRule |
130
|
|
|
*/ |
131
|
3 |
|
public function orderBy($column, $order=null) { |
132
|
3 |
|
$this->order->orderBy($this->context, $column, $order); |
133
|
3 |
|
return $this; |
134
|
|
|
} |
135
|
|
|
// /** |
|
|
|
|
136
|
|
|
// * orderByArgs(['column0', 'column1'=>Sql::ORDER_BY_ASC]) => "ORDER BY column0,column1 ASC" |
137
|
|
|
// * @param array $args |
138
|
|
|
// * @return \PhpBoot\DB\rules\select\OrderByRule |
139
|
|
|
// */ |
140
|
|
|
// public function orderByArgs($args) { |
141
|
|
|
// $this->order->orderByArgs($this->context, $args); |
142
|
|
|
// return $this; |
143
|
|
|
// } |
144
|
|
|
/** |
145
|
|
|
* @var OrderByImpl |
146
|
|
|
*/ |
147
|
|
|
private $order; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
class HavingRule extends OrderByRule |
|
|
|
|
151
|
|
|
{ |
152
|
5 |
|
public function __construct(Context $context, $isTheFirst = true) |
153
|
|
|
{ |
154
|
5 |
|
parent::__construct($context); |
155
|
5 |
|
$this->isTheFirst = $isTheFirst; |
156
|
5 |
|
} |
157
|
|
|
/** |
158
|
|
|
* |
159
|
|
|
* having('SUM(a)=?', 1) => "HAVING SUM(a)=1" |
160
|
|
|
* having('a>?', Sql::raw('now()')) => "HAVING a>now()" |
161
|
|
|
* having('a IN (?)', [1, 2]) => "HAVING a IN (1,2)" |
162
|
|
|
* |
163
|
|
|
* having([ |
164
|
|
|
* 'a'=>1, |
165
|
|
|
* 'b'=>['IN'=>[1,2]] |
166
|
|
|
* 'c'=>['BETWEEN'=>[1,2]] |
167
|
|
|
* 'd'=>['<>'=>1] |
168
|
|
|
* ]) |
169
|
|
|
* |
170
|
|
|
* => |
171
|
|
|
* "HAVING a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1" |
172
|
|
|
* |
173
|
|
|
* @param string|array|callable $expr |
174
|
|
|
* @param string $_ |
175
|
|
|
* @return \PhpBoot\DB\rules\select\HavingRule |
176
|
|
|
*/ |
177
|
4 |
|
public function having($expr, $_=null) { |
|
|
|
|
178
|
4 |
|
if(is_callable($expr)){ |
179
|
|
|
$callback = function ($context)use($expr){ |
180
|
1 |
|
$rule = new ScopedQuery($context); |
181
|
1 |
|
$expr($rule); |
182
|
1 |
|
}; |
183
|
1 |
|
$expr = $callback; |
184
|
1 |
|
} |
185
|
4 |
|
if($this->isTheFirst){ |
186
|
4 |
|
WhereImpl::where($this->context, 'HAVING', $expr, array_slice(func_get_args(), 1)); |
187
|
4 |
|
}else{ |
188
|
2 |
|
WhereImpl::where($this->context, 'AND', $expr, array_slice(func_get_args(), 1)); |
189
|
|
|
} |
190
|
|
|
|
191
|
4 |
|
return new HavingRule($this->context, false); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* |
196
|
|
|
* orHaving('SUM(a)=?', 1) => "OR SUM(a)=1" |
197
|
|
|
* orHaving('a>?', Sql::raw('now()')) => "OR a>now()" |
198
|
|
|
* orHaving('a IN (?)', [1, 2]) => "OR a IN (1,2)" |
199
|
|
|
* |
200
|
|
|
* orHaving([ |
201
|
|
|
* 'a'=>1, |
202
|
|
|
* 'b'=>['IN'=>[1,2]] |
203
|
|
|
* 'c'=>['BETWEEN'=>[1,2]] |
204
|
|
|
* 'd'=>['<>'=>1] |
205
|
|
|
* ]) |
206
|
|
|
* |
207
|
|
|
* => |
208
|
|
|
* "OR (a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1)" |
209
|
|
|
* |
210
|
|
|
* @param string|array|callable $expr |
211
|
|
|
* @param string $_ |
212
|
|
|
* @return \PhpBoot\DB\rules\select\HavingRule |
213
|
|
|
*/ |
214
|
1 |
|
public function orHaving($expr, $_=null) { |
|
|
|
|
215
|
1 |
|
if(is_callable($expr)){ |
216
|
|
|
$callback = function ($context)use($expr){ |
217
|
|
|
$rule = new ScopedQuery($context); |
218
|
|
|
$expr($rule); |
219
|
|
|
}; |
220
|
|
|
$expr = $callback; |
221
|
|
|
} |
222
|
1 |
|
WhereImpl::where($this->context, 'OR', $expr, array_slice(func_get_args(), 1)); |
223
|
1 |
|
return new HavingRule($this->context, false); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
private $isTheFirst; |
227
|
|
|
} |
228
|
|
|
class GroupByRule extends OrderByRule |
|
|
|
|
229
|
|
|
{ |
230
|
|
|
/** |
231
|
|
|
* groupBy('column') => "GROUP BY column" |
232
|
|
|
* @param string $column |
233
|
|
|
* @return \PhpBoot\DB\rules\select\HavingRule |
234
|
|
|
*/ |
235
|
5 |
|
public function groupBy($column) { |
236
|
5 |
|
GroupByImpl::groupBy($this->context, $column); |
237
|
5 |
|
return new HavingRule($this->context); |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
class WhereRule extends GroupByRule |
|
|
|
|
242
|
|
|
{ |
243
|
27 |
|
public function __construct(Context $context, $isTheFirst = true) |
244
|
|
|
{ |
245
|
27 |
|
parent::__construct($context); |
246
|
27 |
|
$this->isTheFirst = $isTheFirst; |
247
|
27 |
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* where('a=?', 1) => "WHERE a=1" |
251
|
|
|
* where('a=?', Sql::raw('now()')) => "WHERE a=now()" |
252
|
|
|
* where('a IN (?)', [1, 2]) => "WHERE a IN (1,2)" |
253
|
|
|
* where([ |
254
|
|
|
* 'a'=>1, |
255
|
|
|
* 'b'=>['IN'=>[1,2]] |
256
|
|
|
* 'c'=>['BETWEEN'=>[1,2]] |
257
|
|
|
* 'd'=>['<>'=>1] |
258
|
|
|
* ]) |
259
|
|
|
* => |
260
|
|
|
* "WHERE a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1" |
261
|
|
|
* |
262
|
|
|
* @param string|array|callable $conditions |
263
|
|
|
* @param mixed $_ |
264
|
|
|
* @return \PhpBoot\DB\rules\select\NextWhereRule |
265
|
|
|
*/ |
266
|
12 |
|
public function where($conditions=null, $_=null) { |
|
|
|
|
267
|
12 |
|
if(is_callable($conditions)){ |
268
|
1 |
|
$callback = function ($context)use($conditions){ |
269
|
1 |
|
$rule = new ScopedQuery($context); |
270
|
1 |
|
$conditions($rule); |
271
|
1 |
|
}; |
272
|
1 |
|
$conditions = $callback; |
273
|
1 |
|
} |
274
|
12 |
|
if($this->isTheFirst){ |
275
|
12 |
|
WhereImpl::where($this->context, 'WHERE' ,$conditions, array_slice(func_get_args(), 1)); |
276
|
12 |
|
}else{ |
277
|
2 |
|
WhereImpl::where($this->context, 'AND', $conditions, array_slice(func_get_args(), 1)); |
278
|
|
|
} |
279
|
12 |
|
return new NextWhereRule($this->context, false); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
protected $isTheFirst; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
|
286
|
|
View Code Duplication |
class NextWhereRule extends WhereRule |
|
|
|
|
287
|
|
|
{ |
288
|
|
|
/** |
289
|
|
|
* orWhere('a=?', 1) => "OR a=1" |
290
|
|
|
* orWhere('a=?', Sql::raw('now()')) => "OR a=now()" |
291
|
|
|
* orWhere('a IN (?)', [1, 2]) => "OR a IN (1,2)" |
292
|
|
|
* orWhere([ |
293
|
|
|
* 'a'=>1, |
294
|
|
|
* 'b'=>['IN'=>[1,2]] |
295
|
|
|
* 'c'=>['BETWEEN'=>[1,2]] |
296
|
|
|
* 'd'=>['<>'=>1] |
297
|
|
|
* ]) |
298
|
|
|
* => |
299
|
|
|
* "OR (a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1)" |
300
|
|
|
* |
301
|
|
|
* @param string|array|callable $conditions |
302
|
|
|
* @param mixed $_ |
303
|
|
|
* @return \PhpBoot\DB\rules\select\NextWhereRule |
304
|
|
|
* |
305
|
|
|
* @TODO orWhere 只能跟在 Where 后 |
306
|
|
|
*/ |
307
|
1 |
|
public function orWhere($conditions=null, $_=null) { |
|
|
|
|
308
|
1 |
|
if(is_callable($conditions)){ |
309
|
|
|
$callback = function ($context)use($conditions){ |
310
|
|
|
$rule = new ScopedQuery($context); |
311
|
|
|
$conditions($rule); |
312
|
|
|
}; |
313
|
|
|
$conditions = $callback; |
314
|
|
|
} |
315
|
1 |
|
WhereImpl::where($this->context, 'OR', $conditions, array_slice(func_get_args(), 1)); |
316
|
1 |
|
return new NextWhereRule($this->context, false); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
class JoinRule extends WhereRule |
|
|
|
|
322
|
|
|
{ |
323
|
|
|
/** |
324
|
|
|
* join('table1')->on('table0.id=table1.id') => "JOIN table1 ON table0.id=table1.id" |
325
|
|
|
* @param string $table |
326
|
|
|
* @return \PhpBoot\DB\rules\select\JoinOnRule |
327
|
|
|
*/ |
328
|
2 |
|
public function join($table){ |
329
|
2 |
|
JoinImpl::join($this->context,null, $table); |
330
|
2 |
|
return new JoinOnRule($this->context); |
331
|
|
|
} |
332
|
|
|
/** |
333
|
|
|
* leftJoin('table1')->on('table0.id=table1.id') => "LEFT JOIN table1 ON table0.id=table1.id" |
334
|
|
|
* @param string $table |
335
|
|
|
* @return \PhpBoot\DB\rules\select\JoinOnRule |
336
|
|
|
*/ |
337
|
1 |
|
public function leftJoin($table){ |
338
|
1 |
|
JoinImpl::join($this->context,'LEFT', $table); |
339
|
1 |
|
return new JoinOnRule($this->context); |
340
|
|
|
} |
341
|
|
|
/** |
342
|
|
|
* rightJoin('table1')->on('table0.id=table1.id') => "RIGHT JOIN table1 ON table0.id=table1.id" |
343
|
|
|
* @param string $table |
344
|
|
|
* @return \PhpBoot\DB\rules\select\JoinOnRule |
345
|
|
|
*/ |
346
|
1 |
|
public function rightJoin($table) { |
347
|
1 |
|
JoinImpl::join($this->context,'RIGHT', $table); |
348
|
1 |
|
return new JoinOnRule($this->context); |
349
|
|
|
} |
350
|
|
|
/** |
351
|
|
|
* innerJoin('table1')->on('table0.id=table1.id') => "INNER JOIN table1 ON table0.id=table1.id" |
352
|
|
|
* @param string $table |
353
|
|
|
* @return \PhpBoot\DB\rules\select\JoinOnRule |
354
|
|
|
*/ |
355
|
1 |
|
public function innerJoin($table) { |
356
|
1 |
|
JoinImpl::join($this->context,'INNER', $table); |
357
|
1 |
|
return new JoinOnRule($this->context); |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
class JoinOnRule extends BasicRule |
|
|
|
|
362
|
|
|
{ |
363
|
|
|
/** |
364
|
|
|
* join('table1')->on('table0.id=table1.id') => "JOIN table1 ON table0.id=table1.id" |
365
|
|
|
* @param string $condition |
366
|
|
|
* @return \PhpBoot\DB\rules\select\JoinRule |
367
|
|
|
*/ |
368
|
5 |
|
public function on($condition){ |
369
|
5 |
|
JoinOnImpl::on($this->context, $condition); |
370
|
5 |
|
return new JoinRule($this->context); |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.