|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
namespace voku\db; |
|
6
|
|
|
|
|
7
|
|
|
use Arrayy\Arrayy; |
|
8
|
|
|
use voku\db\exceptions\ActiveRecordException; |
|
9
|
|
|
use voku\db\exceptions\FetchingException; |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* A simple implement of active record via Arrayy. |
|
13
|
|
|
* |
|
14
|
|
|
* @method $this select(string $dbProperty) |
|
15
|
|
|
* @method $this eq(string $dbProperty, string | int | null $value = null) |
|
16
|
|
|
* @method $this from(string $table) |
|
17
|
|
|
* @method $this where(string $where) |
|
18
|
|
|
* @method $this having(string $having) |
|
19
|
|
|
* @method $this limit(int $start, int | null $end = null) |
|
20
|
|
|
* |
|
21
|
|
|
* @method $this equal(string $dbProperty, string $value) |
|
22
|
|
|
* @method $this notEqual(string $dbProperty, string $value) |
|
23
|
|
|
* @method $this ne(string $dbProperty, string $value) |
|
24
|
|
|
* @method $this greaterThan(string $dbProperty, int $value) |
|
25
|
|
|
* @method $this gt(string $dbProperty, int $value) |
|
26
|
|
|
* @method $this lessThan(string $dbProperty, int $value) |
|
27
|
|
|
* @method $this lt(string $dbProperty, int $value) |
|
28
|
|
|
* @method $this greaterThanOrEqual(string $dbProperty, int $value) |
|
29
|
|
|
* @method $this ge(string $dbProperty, int $value) |
|
30
|
|
|
* @method $this gte(string $dbProperty, int $value) |
|
31
|
|
|
* @method $this lessThanOrEqual(string $dbProperty, int $value) |
|
32
|
|
|
* @method $this le(string $dbProperty, int $value) |
|
33
|
|
|
* @method $this lte(string $dbProperty, int $value) |
|
34
|
|
|
* @method $this between(string $dbProperty, array $value) |
|
35
|
|
|
* @method $this like(string $dbProperty, string $value) |
|
36
|
|
|
* @method $this in(string $dbProperty, array $value) |
|
37
|
|
|
* @method $this notIn(string $dbProperty, array $value) |
|
38
|
|
|
* @method $this isnull(string $dbProperty) |
|
39
|
|
|
* @method $this isNotNull(string $dbProperty) |
|
40
|
|
|
* @method $this notNull(string $dbProperty) |
|
41
|
|
|
*/ |
|
42
|
|
|
abstract class ActiveRecord extends Arrayy |
|
43
|
|
|
{ |
|
44
|
|
|
/** |
|
45
|
|
|
* @var DB static |
|
46
|
|
|
*/ |
|
47
|
|
|
protected static $db; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var array <p>Mapping the function name and the operator, to build Expressions in WHERE condition.</p> |
|
51
|
|
|
* |
|
52
|
|
|
* call the function like this: |
|
53
|
|
|
* <pre> |
|
54
|
|
|
* $user->isNotNull()->eq('id', 1); |
|
55
|
|
|
* </pre> |
|
56
|
|
|
* |
|
57
|
|
|
* the result in SQL: |
|
58
|
|
|
* <pre> |
|
59
|
|
|
* WHERE user.id IS NOT NULL AND user.id = :ph1 |
|
60
|
|
|
* </pre> |
|
61
|
|
|
*/ |
|
62
|
|
|
protected static $operators = [ |
|
63
|
|
|
'equal' => '=', |
|
64
|
|
|
'eq' => '=', |
|
65
|
|
|
'notequal' => '<>', |
|
66
|
|
|
'ne' => '<>', |
|
67
|
|
|
'greaterthan' => '>', |
|
68
|
|
|
'gt' => '>', |
|
69
|
|
|
'lessthan' => '<', |
|
70
|
|
|
'lt' => '<', |
|
71
|
|
|
'greaterthanorequal' => '>=', |
|
72
|
|
|
'ge' => '>=', |
|
73
|
|
|
'gte' => '>=', |
|
74
|
|
|
'lessthanorequal' => '<=', |
|
75
|
|
|
'le' => '<=', |
|
76
|
|
|
'lte' => '<=', |
|
77
|
|
|
'between' => 'BETWEEN', |
|
78
|
|
|
'like' => 'LIKE', |
|
79
|
|
|
'in' => 'IN', |
|
80
|
|
|
'notin' => 'NOT IN', |
|
81
|
|
|
'isnull' => 'IS NULL', |
|
82
|
|
|
'isnotnull' => 'IS NOT NULL', |
|
83
|
|
|
'notnull' => 'IS NOT NULL', |
|
84
|
|
|
]; |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* @var array <p>Part of the SQL, mapping the function name and the operator to build SQL Part.</p> |
|
88
|
|
|
* |
|
89
|
|
|
* <br /> |
|
90
|
|
|
* |
|
91
|
|
|
* call the function like this: |
|
92
|
|
|
* <pre> |
|
93
|
|
|
* $user->orderBy('id DESC', 'name ASC')->limit(2, 1); |
|
94
|
|
|
* </pre> |
|
95
|
|
|
* |
|
96
|
|
|
* the result in SQL: |
|
97
|
|
|
* <pre> |
|
98
|
|
|
* ORDER BY id DESC, name ASC LIMIT 2,1 |
|
99
|
|
|
* </pre> |
|
100
|
|
|
*/ |
|
101
|
|
|
protected $sqlParts = [ |
|
102
|
|
|
'select' => 'SELECT', |
|
103
|
|
|
'from' => 'FROM', |
|
104
|
|
|
'set' => 'SET', |
|
105
|
|
|
'where' => 'WHERE', |
|
106
|
|
|
'group' => 'GROUP BY', |
|
107
|
|
|
'having' => 'HAVING', |
|
108
|
|
|
'order' => 'ORDER BY', |
|
109
|
|
|
'limit' => 'LIMIT', |
|
110
|
|
|
'top' => 'TOP', |
|
111
|
|
|
]; |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* @var array <p>The default sql expressions values.</p> |
|
115
|
|
|
*/ |
|
116
|
|
|
protected $defaultSqlExpressions = [ |
|
117
|
|
|
'expressions' => [], |
|
118
|
|
|
'wrap' => false, |
|
119
|
|
|
'select' => null, |
|
120
|
|
|
'insert' => null, |
|
121
|
|
|
'update' => null, |
|
122
|
|
|
'set' => null, |
|
123
|
|
|
'delete' => 'DELETE ', |
|
124
|
|
|
'join' => null, |
|
125
|
|
|
'from' => null, |
|
126
|
|
|
'values' => null, |
|
127
|
|
|
'where' => null, |
|
128
|
|
|
'having' => null, |
|
129
|
|
|
'limit' => null, |
|
130
|
|
|
'order' => null, |
|
131
|
|
|
'group' => null, |
|
132
|
|
|
]; |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* @var array <p>Stored the Expressions of the SQL.</p> |
|
136
|
|
|
*/ |
|
137
|
|
|
protected $sqlExpressions = []; |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* @var string <p>The table name in database.</p> |
|
141
|
|
|
*/ |
|
142
|
|
|
protected $table; |
|
143
|
|
|
|
|
144
|
|
|
/** |
|
145
|
|
|
* @var string <p>The primary key of this ActiveRecord, just support single primary key.</p> |
|
146
|
|
|
*/ |
|
147
|
|
|
protected $primaryKeyName = 'id'; |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* @var array <p>Stored the dirty data of this object, when call "insert" or "update" function, will write this data |
|
151
|
|
|
* into database.</p> |
|
152
|
|
|
*/ |
|
153
|
|
|
protected $dirty = []; |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* @var bool |
|
157
|
|
|
*/ |
|
158
|
|
|
protected static $new_data_are_dirty = true; |
|
159
|
|
|
|
|
160
|
|
|
/** |
|
161
|
|
|
* @var array <p>Stored the params will bind to SQL when call DB->query().</p> |
|
162
|
|
|
*/ |
|
163
|
|
|
protected $params = []; |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* @var ActiveRecordExpressions[] <p>Stored the configure of the relation, or target of the relation.</p> |
|
167
|
|
|
*/ |
|
168
|
|
|
protected $relations = []; |
|
169
|
|
|
|
|
170
|
|
|
/** |
|
171
|
|
|
* @var int <p>The count of bind params, using this count and const "PREFIX" (:ph) to generate place holder in |
|
172
|
|
|
* SQL.</p> |
|
173
|
|
|
*/ |
|
174
|
|
|
private static $count = 0; |
|
175
|
|
|
|
|
176
|
|
|
const BELONGS_TO = 'belongs_to'; |
|
177
|
|
|
const HAS_MANY = 'has_many'; |
|
178
|
|
|
const HAS_ONE = 'has_one'; |
|
179
|
|
|
|
|
180
|
|
|
const PREFIX = ':active_record'; |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* @return array |
|
184
|
|
|
*/ |
|
185
|
|
|
public function getParams(): array |
|
186
|
|
|
{ |
|
187
|
|
|
return $this->params; |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
/** |
|
191
|
|
|
* @return string |
|
192
|
|
|
*/ |
|
193
|
|
|
public function getPrimaryKeyName(): string |
|
194
|
|
|
{ |
|
195
|
|
|
return $this->primaryKeyName; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
/** |
|
199
|
|
|
* @return mixed|null |
|
200
|
|
|
*/ |
|
201
|
13 |
|
public function getPrimaryKey() |
|
202
|
|
|
{ |
|
203
|
13 |
|
$id = $this->{$this->primaryKeyName}; |
|
204
|
13 |
|
if ($id) { |
|
205
|
12 |
|
return $id; |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
1 |
|
return null; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* @param mixed $primaryKey |
|
213
|
|
|
* @param bool $dirty |
|
214
|
|
|
* |
|
215
|
|
|
* @return $this |
|
216
|
|
|
*/ |
|
217
|
1 |
|
public function setPrimaryKey($primaryKey, bool $dirty = true) |
|
218
|
|
|
{ |
|
219
|
1 |
|
if ($dirty === true) { |
|
220
|
1 |
|
$this->dirty[$this->primaryKeyName] = $primaryKey; |
|
221
|
|
|
} else { |
|
222
|
|
|
$this->array[$this->primaryKeyName] = $primaryKey; |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
1 |
|
return $this; |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
/** |
|
229
|
|
|
* @return string |
|
230
|
|
|
*/ |
|
231
|
|
|
public function getTable(): string |
|
232
|
|
|
{ |
|
233
|
|
|
return $this->table; |
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
/** |
|
237
|
|
|
* Function to reset the $params and $sqlExpressions. |
|
238
|
|
|
* |
|
239
|
|
|
* @return $this |
|
240
|
|
|
*/ |
|
241
|
23 |
|
public function reset() |
|
242
|
|
|
{ |
|
243
|
23 |
|
$this->params = []; |
|
244
|
23 |
|
$this->sqlExpressions = []; |
|
245
|
|
|
|
|
246
|
23 |
|
return $this; |
|
247
|
|
|
} |
|
248
|
|
|
|
|
249
|
|
|
/** |
|
250
|
|
|
* Reset the dirty data. |
|
251
|
|
|
* |
|
252
|
|
|
* @return $this |
|
253
|
|
|
*/ |
|
254
|
6 |
|
public function resetDirty() |
|
255
|
|
|
{ |
|
256
|
6 |
|
$this->dirty = []; |
|
257
|
|
|
|
|
258
|
6 |
|
return $this; |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
/** |
|
262
|
|
|
* set the DB connection. |
|
263
|
|
|
* |
|
264
|
|
|
* @param DB $db |
|
265
|
|
|
*/ |
|
266
|
|
|
public static function setDb(DB $db) |
|
267
|
|
|
{ |
|
268
|
|
|
self::$db = $db; |
|
269
|
|
|
} |
|
270
|
|
|
|
|
271
|
|
|
/** |
|
272
|
|
|
* Function to find one record and assign in to current object. |
|
273
|
|
|
* |
|
274
|
|
|
* @param mixed $id <p> |
|
275
|
|
|
* If call this function using this param, we will find the record by using this id. |
|
276
|
|
|
* If not set, just find the first record in database. |
|
277
|
|
|
* </p> |
|
278
|
|
|
* |
|
279
|
|
|
* @return false|$this <p> |
|
280
|
|
|
* If we could find the record, assign in to current object and return it, |
|
281
|
|
|
* otherwise return "false". |
|
282
|
|
|
* </p> |
|
283
|
|
|
*/ |
|
284
|
11 |
View Code Duplication |
public function fetch($id = null) |
|
|
|
|
|
|
285
|
|
|
{ |
|
286
|
11 |
|
if ($id) { |
|
287
|
6 |
|
$this->reset()->eq($this->primaryKeyName, $id); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
11 |
|
return self::query( |
|
291
|
11 |
|
$this->limit(1)->_buildSql( |
|
|
|
|
|
|
292
|
|
|
[ |
|
293
|
11 |
|
'select', |
|
294
|
|
|
'from', |
|
295
|
|
|
'join', |
|
296
|
|
|
'where', |
|
297
|
|
|
'group', |
|
298
|
|
|
'having', |
|
299
|
|
|
'order', |
|
300
|
|
|
'limit', |
|
301
|
|
|
] |
|
302
|
|
|
), |
|
303
|
11 |
|
$this->params, |
|
304
|
11 |
|
$this->reset(), |
|
|
|
|
|
|
305
|
11 |
|
true |
|
306
|
|
|
); |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
/** |
|
310
|
|
|
* @param string $query |
|
311
|
|
|
* |
|
312
|
|
|
* @return $this[] |
|
313
|
|
|
*/ |
|
314
|
1 |
|
public function fetchManyByQuery(string $query): array |
|
315
|
|
|
{ |
|
316
|
1 |
|
$list = $this->fetchByQuery($query); |
|
317
|
|
|
|
|
318
|
1 |
|
if (!$list || empty($list)) { |
|
319
|
|
|
return []; |
|
320
|
|
|
} |
|
321
|
|
|
|
|
322
|
1 |
|
return $list; |
|
323
|
|
|
} |
|
324
|
|
|
|
|
325
|
|
|
/** |
|
326
|
|
|
* @param string $query |
|
327
|
|
|
* |
|
328
|
|
|
* @return $this|null |
|
329
|
|
|
*/ |
|
330
|
1 |
|
public function fetchOneByQuery(string $query) |
|
331
|
|
|
{ |
|
332
|
1 |
|
$list = $this->fetchByQuery($query); |
|
333
|
|
|
|
|
334
|
1 |
|
if (!$list || empty($list)) { |
|
335
|
|
|
return null; |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
1 |
|
if (\is_array($list) && \count($list) > 0) { |
|
339
|
1 |
|
$this->array = $list[0]->getArray(); |
|
340
|
|
|
} else { |
|
341
|
|
|
$this->array = $list->getArray(); |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
1 |
|
return $this; |
|
345
|
|
|
} |
|
346
|
|
|
|
|
347
|
|
|
/** |
|
348
|
|
|
* @param mixed $id |
|
349
|
|
|
* |
|
350
|
|
|
* @return $this |
|
351
|
|
|
* |
|
352
|
|
|
* @throws FetchingException <p>Will be thrown, if we can not find the id.</p> |
|
353
|
|
|
*/ |
|
354
|
2 |
|
public function fetchById($id) |
|
355
|
|
|
{ |
|
356
|
2 |
|
$obj = $this->fetchByIdIfExists($id); |
|
357
|
2 |
|
if ($obj === null) { |
|
358
|
1 |
|
throw new FetchingException("No row with primary key '$id' in table '$this->table'."); |
|
359
|
|
|
} |
|
360
|
|
|
|
|
361
|
1 |
|
return $obj; |
|
|
|
|
|
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
|
|
/** |
|
365
|
|
|
* @param mixed $id |
|
366
|
|
|
* |
|
367
|
|
|
* @return $this|null |
|
368
|
|
|
*/ |
|
369
|
4 |
|
public function fetchByIdIfExists($id) |
|
370
|
|
|
{ |
|
371
|
4 |
|
$list = $this->fetch($id); |
|
372
|
|
|
|
|
373
|
4 |
|
if (!$list || $list->isEmpty()) { |
|
374
|
2 |
|
return null; |
|
375
|
|
|
} |
|
376
|
|
|
|
|
377
|
2 |
|
return $list; |
|
378
|
|
|
} |
|
379
|
|
|
|
|
380
|
|
|
/** |
|
381
|
|
|
* @param array $ids |
|
382
|
|
|
* |
|
383
|
|
|
* @return $this[] |
|
384
|
|
|
*/ |
|
385
|
2 |
|
public function fetchByIds(array $ids): array |
|
386
|
|
|
{ |
|
387
|
2 |
|
if (empty($ids)) { |
|
388
|
|
|
return []; |
|
389
|
|
|
} |
|
390
|
|
|
|
|
391
|
2 |
|
$list = $this->fetchAll($ids); |
|
392
|
2 |
|
if (\is_array($list) && \count($list) > 0) { |
|
393
|
1 |
|
return $list; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
1 |
|
return []; |
|
397
|
|
|
} |
|
398
|
|
|
|
|
399
|
|
|
/** |
|
400
|
|
|
* @param string $query |
|
401
|
|
|
* |
|
402
|
|
|
* @return $this[]|$this |
|
403
|
|
|
*/ |
|
404
|
2 |
|
public function fetchByQuery(string $query) |
|
405
|
|
|
{ |
|
406
|
2 |
|
$list = self::query( |
|
407
|
2 |
|
$query, |
|
408
|
2 |
|
$this->params, |
|
409
|
2 |
|
$this->reset() |
|
|
|
|
|
|
410
|
|
|
); |
|
411
|
|
|
|
|
412
|
2 |
|
if (\is_array($list)) { |
|
413
|
2 |
|
if (\count($list) === 0) { |
|
414
|
|
|
return []; |
|
415
|
|
|
} |
|
416
|
|
|
|
|
417
|
2 |
|
return $list; |
|
418
|
|
|
} |
|
419
|
|
|
|
|
420
|
|
|
$this->array = $list->getArray(); |
|
421
|
|
|
|
|
422
|
|
|
return $this; |
|
423
|
|
|
} |
|
424
|
|
|
|
|
425
|
|
|
/** |
|
426
|
|
|
* @param array $ids |
|
427
|
|
|
* |
|
428
|
|
|
* @return $this[] |
|
429
|
|
|
*/ |
|
430
|
1 |
|
public function fetchByIdsPrimaryKeyAsArrayIndex(array $ids): array |
|
431
|
|
|
{ |
|
432
|
1 |
|
$result = $this->fetchAll($ids); |
|
433
|
|
|
|
|
434
|
1 |
|
$resultNew = []; |
|
435
|
1 |
|
foreach ($result as $item) { |
|
436
|
1 |
|
$resultNew[$item->getPrimaryKey()] = $item; |
|
437
|
|
|
} |
|
438
|
|
|
|
|
439
|
1 |
|
return $resultNew; |
|
440
|
|
|
} |
|
441
|
|
|
|
|
442
|
|
|
/** |
|
443
|
|
|
* Function to find all records in database. |
|
444
|
|
|
* |
|
445
|
|
|
* @param array|null $ids <p> |
|
446
|
|
|
* If call this function using this param, we will find the record by using this id's. |
|
447
|
|
|
* If not set, just find all records in database. |
|
448
|
|
|
* </p> |
|
449
|
|
|
* |
|
450
|
|
|
* @return $this[] |
|
451
|
|
|
*/ |
|
452
|
6 |
View Code Duplication |
public function fetchAll(array $ids = null): array |
|
|
|
|
|
|
453
|
|
|
{ |
|
454
|
6 |
|
if ($ids) { |
|
455
|
3 |
|
$this->reset()->in($this->primaryKeyName, $ids); |
|
456
|
|
|
} |
|
457
|
|
|
|
|
458
|
6 |
|
return self::query( |
|
459
|
6 |
|
$this->_buildSql( |
|
460
|
|
|
[ |
|
461
|
6 |
|
'select', |
|
462
|
|
|
'from', |
|
463
|
|
|
'join', |
|
464
|
|
|
'where', |
|
465
|
|
|
'groupBy', |
|
466
|
|
|
'having', |
|
467
|
|
|
'orderBy', |
|
468
|
|
|
'limit', |
|
469
|
|
|
] |
|
470
|
|
|
), |
|
471
|
6 |
|
$this->params, |
|
472
|
6 |
|
$this->reset() |
|
|
|
|
|
|
473
|
|
|
); |
|
474
|
|
|
} |
|
475
|
|
|
|
|
476
|
|
|
/** |
|
477
|
|
|
* Function to delete current record in database. |
|
478
|
|
|
* |
|
479
|
|
|
* @return bool |
|
480
|
|
|
*/ |
|
481
|
1 |
|
public function delete(): bool |
|
482
|
|
|
{ |
|
483
|
1 |
|
$return = self::execute( |
|
484
|
1 |
|
$this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql( |
|
485
|
|
|
[ |
|
486
|
1 |
|
'delete', |
|
487
|
|
|
'from', |
|
488
|
|
|
'where', |
|
489
|
|
|
] |
|
490
|
|
|
), |
|
491
|
1 |
|
$this->params |
|
492
|
|
|
); |
|
493
|
|
|
|
|
494
|
1 |
|
return $return !== false; |
|
495
|
|
|
} |
|
496
|
|
|
|
|
497
|
|
|
/** |
|
498
|
|
|
* @param string $primaryKeyName |
|
499
|
|
|
* |
|
500
|
|
|
* @return $this |
|
501
|
|
|
*/ |
|
502
|
|
|
public function setPrimaryKeyName(string $primaryKeyName) |
|
503
|
|
|
{ |
|
504
|
|
|
$this->primaryKeyName = $primaryKeyName; |
|
505
|
|
|
|
|
506
|
|
|
return $this; |
|
507
|
|
|
} |
|
508
|
|
|
|
|
509
|
|
|
/** |
|
510
|
|
|
* @param string $table |
|
511
|
|
|
*/ |
|
512
|
|
|
public function setTable(string $table) |
|
513
|
|
|
{ |
|
514
|
|
|
$this->table = $table; |
|
515
|
|
|
} |
|
516
|
|
|
|
|
517
|
|
|
/** |
|
518
|
|
|
* Function to build update SQL, and update current record in database, just write the dirty data into database. |
|
519
|
|
|
* |
|
520
|
|
|
* @return bool|int <p> |
|
521
|
|
|
* If update was successful, it will return the affected rows as int, |
|
522
|
|
|
* otherwise it will return false or true (if there are no dirty data). |
|
523
|
|
|
* </p> |
|
524
|
|
|
*/ |
|
525
|
2 |
|
public function update() |
|
526
|
|
|
{ |
|
527
|
2 |
|
if (\count($this->dirty) == 0) { |
|
528
|
|
|
return true; |
|
529
|
|
|
} |
|
530
|
|
|
|
|
531
|
2 |
|
foreach ($this->dirty as $field => $value) { |
|
532
|
2 |
|
$this->addCondition($field, '=', $value, ',', 'set'); |
|
533
|
|
|
} |
|
534
|
|
|
|
|
535
|
2 |
|
$result = self::execute( |
|
536
|
2 |
|
$this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql( |
|
537
|
|
|
[ |
|
538
|
2 |
|
'update', |
|
539
|
|
|
'set', |
|
540
|
|
|
'where', |
|
541
|
|
|
] |
|
542
|
|
|
), |
|
543
|
2 |
|
$this->params |
|
544
|
|
|
); |
|
545
|
2 |
|
if ($result !== false) { |
|
546
|
2 |
|
$this->resetDirty(); |
|
547
|
2 |
|
$this->reset(); |
|
548
|
|
|
|
|
549
|
2 |
|
return $result; |
|
550
|
|
|
} |
|
551
|
|
|
|
|
552
|
|
|
return false; |
|
553
|
|
|
} |
|
554
|
|
|
|
|
555
|
|
|
/** |
|
556
|
|
|
* @return $this |
|
557
|
|
|
*/ |
|
558
|
1 |
|
public static function fetchEmpty() |
|
559
|
|
|
{ |
|
560
|
1 |
|
$class = static::class; |
|
561
|
|
|
|
|
562
|
1 |
|
return new $class; |
|
563
|
|
|
} |
|
564
|
|
|
|
|
565
|
|
|
/** |
|
566
|
|
|
* Function to build insert SQL, and insert current record into database. |
|
567
|
|
|
* |
|
568
|
|
|
* @return bool|int <p> |
|
569
|
|
|
* If insert was successful, it will return the new id, |
|
570
|
|
|
* otherwise it will return false or true (if there are no dirty data). |
|
571
|
|
|
* </p> |
|
572
|
|
|
*/ |
|
573
|
4 |
|
public function insert() |
|
574
|
|
|
{ |
|
575
|
4 |
|
if (!self::$db instanceof DB) { |
|
576
|
|
|
self::$db = DB::getInstance(); |
|
|
|
|
|
|
577
|
|
|
} |
|
578
|
|
|
|
|
579
|
4 |
|
if (\count($this->dirty) === 0) { |
|
580
|
|
|
return true; |
|
581
|
|
|
} |
|
582
|
|
|
|
|
583
|
4 |
|
$value = $this->_filterParam($this->dirty); |
|
584
|
4 |
|
$this->insert = new ActiveRecordExpressions( |
|
|
|
|
|
|
585
|
|
|
[ |
|
586
|
4 |
|
'operator' => 'INSERT INTO ' . $this->table, |
|
587
|
4 |
|
'target' => new ActiveRecordExpressionsWrap(['target' => \array_keys($this->dirty)]), |
|
588
|
|
|
] |
|
589
|
|
|
); |
|
590
|
4 |
|
$this->values = new ActiveRecordExpressions( |
|
|
|
|
|
|
591
|
|
|
[ |
|
592
|
4 |
|
'operator' => 'VALUES', |
|
593
|
4 |
|
'target' => new ActiveRecordExpressionsWrap(['target' => $value]), |
|
594
|
|
|
] |
|
595
|
|
|
); |
|
596
|
|
|
|
|
597
|
4 |
|
$result = self::execute($this->_buildSql(['insert', 'values']), $this->params); |
|
598
|
4 |
|
if ($result !== false) { |
|
599
|
4 |
|
$this->{$this->primaryKeyName} = $result; |
|
600
|
|
|
|
|
601
|
4 |
|
$this->resetDirty(); |
|
602
|
4 |
|
$this->reset(); |
|
603
|
|
|
|
|
604
|
4 |
|
return $result; |
|
605
|
|
|
} |
|
606
|
|
|
|
|
607
|
|
|
return false; |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
/** |
|
611
|
|
|
* Helper function to copy an existing active record (and insert it into the database). |
|
612
|
|
|
* |
|
613
|
|
|
* @param bool $insert |
|
614
|
|
|
* |
|
615
|
|
|
* @return $this |
|
616
|
|
|
*/ |
|
617
|
1 |
|
public function copy(bool $insert = true) |
|
618
|
|
|
{ |
|
619
|
1 |
|
$new = clone $this; |
|
620
|
|
|
|
|
621
|
1 |
|
if ($insert) { |
|
622
|
1 |
|
$new->setPrimaryKey(null); |
|
623
|
1 |
|
$id = $new->insert(); |
|
624
|
1 |
|
$new->setPrimaryKey($id); |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
1 |
|
return $new; |
|
628
|
|
|
} |
|
629
|
|
|
|
|
630
|
|
|
/** |
|
631
|
|
|
* Helper function to exec sql. |
|
632
|
|
|
* |
|
633
|
|
|
* @param string $sql <p>The SQL need to be execute.</p> |
|
634
|
|
|
* @param array $param <p>The param will be bind to the sql statement.</p> |
|
635
|
|
|
* |
|
636
|
|
|
* @return bool|int|Result <p> |
|
637
|
|
|
* "Result" by "<b>SELECT</b>"-queries<br /> |
|
638
|
|
|
* "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br /> |
|
639
|
|
|
* "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br /> |
|
640
|
|
|
* "true" by e.g. "DROP"-queries<br /> |
|
641
|
|
|
* "false" on error |
|
642
|
|
|
* </p> |
|
643
|
|
|
*/ |
|
644
|
24 |
|
public static function execute(string $sql, array $param = []) |
|
645
|
|
|
{ |
|
646
|
24 |
|
if (!self::$db instanceof DB) { |
|
647
|
1 |
|
self::$db = DB::getInstance(); |
|
|
|
|
|
|
648
|
|
|
} |
|
649
|
|
|
|
|
650
|
24 |
|
return self::$db->query($sql, $param); |
|
651
|
|
|
} |
|
652
|
|
|
|
|
653
|
|
|
/** |
|
654
|
|
|
* Helper function to query one record by sql and params. |
|
655
|
|
|
* |
|
656
|
|
|
* @param string $sql <p> |
|
657
|
|
|
* The SQL query to find the record. |
|
658
|
|
|
* </p> |
|
659
|
|
|
* @param array $param <p> |
|
660
|
|
|
* The param will be bind to the $sql query. |
|
661
|
|
|
* </p> |
|
662
|
|
|
* @param null|self $obj <p> |
|
663
|
|
|
* The object, if find record in database, we will assign the attributes into |
|
664
|
|
|
* this object. |
|
665
|
|
|
* </p> |
|
666
|
|
|
* @param bool $single <p> |
|
667
|
|
|
* If set to true, we will find record and fetch in current object, otherwise |
|
668
|
|
|
* will find all records. |
|
669
|
|
|
* </p> |
|
670
|
|
|
* |
|
671
|
|
|
* @return bool|$this|array |
|
672
|
|
|
*/ |
|
673
|
17 |
|
public static function query(string $sql, array $param = [], self $obj = null, bool $single = false) |
|
674
|
|
|
{ |
|
675
|
17 |
|
$result = self::execute($sql, $param); |
|
676
|
|
|
|
|
677
|
17 |
|
if ($result === false) { |
|
678
|
|
|
return false; |
|
679
|
|
|
} |
|
680
|
|
|
|
|
681
|
17 |
|
$useObject = \is_object($obj); |
|
682
|
17 |
|
if ($useObject === true) { |
|
683
|
17 |
|
$called_class = $obj; |
|
684
|
|
|
} else { |
|
685
|
|
|
$called_class = static::class; |
|
686
|
|
|
} |
|
687
|
|
|
|
|
688
|
17 |
|
self::setNewDataAreDirty(false); |
|
689
|
|
|
|
|
690
|
17 |
|
if ($single) { |
|
691
|
11 |
|
$return = $result->fetchObject($called_class, null, true); |
|
692
|
|
|
} else { |
|
693
|
8 |
|
$return = $result->fetchAllObject($called_class, null); |
|
694
|
|
|
} |
|
695
|
|
|
|
|
696
|
17 |
|
self::setNewDataAreDirty(true); |
|
697
|
|
|
|
|
698
|
17 |
|
return $return; |
|
699
|
|
|
} |
|
700
|
|
|
|
|
701
|
|
|
/** |
|
702
|
|
|
* Helper function to get relation of this object. |
|
703
|
|
|
* There was three types of relations: {BELONGS_TO, HAS_ONE, HAS_MANY} |
|
704
|
|
|
* |
|
705
|
|
|
* @param string $name <p>The name of the relation (the array key from the definition).</p> |
|
706
|
|
|
* |
|
707
|
|
|
* @return mixed |
|
708
|
|
|
* |
|
709
|
|
|
* @throws ActiveRecordException <p>If the relation can't be found .</p> |
|
710
|
|
|
*/ |
|
711
|
3 |
|
protected function &getRelation(string $name) |
|
712
|
|
|
{ |
|
713
|
3 |
|
$relation = $this->relations[$name]; |
|
714
|
|
|
if ( |
|
715
|
3 |
|
$relation instanceof self |
|
716
|
|
|
|| |
|
717
|
|
|
( |
|
718
|
2 |
|
\is_array($relation) |
|
719
|
|
|
&& |
|
720
|
3 |
|
$relation[0] instanceof self |
|
721
|
|
|
) |
|
722
|
|
|
) { |
|
723
|
3 |
|
return $relation; |
|
724
|
|
|
} |
|
725
|
|
|
|
|
726
|
|
|
/* @var $obj ActiveRecord */ |
|
727
|
2 |
|
$obj = new $relation[1]; |
|
728
|
|
|
|
|
729
|
2 |
|
$this->relations[$name] = $obj; |
|
730
|
2 |
|
if (isset($relation[3]) && \is_array($relation[3])) { |
|
731
|
1 |
|
foreach ((array)$relation[3] as $func => $args) { |
|
732
|
1 |
|
\call_user_func_array([$obj, $func], (array)$args); |
|
733
|
|
|
} |
|
734
|
|
|
} |
|
735
|
|
|
|
|
736
|
2 |
|
$backref = $relation[4] ?? ''; |
|
737
|
2 |
|
$relationInstanceOfSelf = ($relation instanceof self); |
|
738
|
|
|
if ( |
|
739
|
2 |
|
$relationInstanceOfSelf === false |
|
740
|
|
|
&& |
|
741
|
2 |
|
self::HAS_ONE == $relation[0] |
|
742
|
|
|
) { |
|
743
|
|
|
|
|
744
|
1 |
|
$this->relations[$name] = $obj->eq((string)$relation[2], $this->{$this->primaryKeyName})->fetch(); |
|
745
|
|
|
|
|
746
|
1 |
|
if ($backref) { |
|
747
|
1 |
|
$this->relations[$name] && $backref && $obj->{$backref} = $this; |
|
748
|
|
|
} |
|
749
|
|
|
|
|
750
|
|
|
} elseif ( |
|
751
|
2 |
|
\is_array($relation) |
|
752
|
|
|
&& |
|
753
|
2 |
|
self::HAS_MANY == $relation[0] |
|
754
|
|
|
) { |
|
755
|
|
|
|
|
756
|
2 |
|
$this->relations[$name] = $obj->eq((string)$relation[2], $this->{$this->primaryKeyName})->fetchAll(); |
|
757
|
2 |
|
if ($backref) { |
|
758
|
1 |
|
foreach ($this->relations[$name] as $o) { |
|
759
|
2 |
|
$o->{$backref} = $this; |
|
760
|
|
|
} |
|
761
|
|
|
} |
|
762
|
|
|
|
|
763
|
|
|
} elseif ( |
|
764
|
2 |
|
$relationInstanceOfSelf === false |
|
765
|
|
|
&& |
|
766
|
2 |
|
self::BELONGS_TO == $relation[0] |
|
767
|
|
|
) { |
|
768
|
|
|
|
|
769
|
2 |
|
$this->relations[$name] = $obj->eq($obj->primaryKeyName, $this->{$relation[2]})->fetch(); |
|
770
|
|
|
|
|
771
|
2 |
|
if ($backref) { |
|
772
|
2 |
|
$this->relations[$name] && $backref && $obj->{$backref} = $this; |
|
773
|
|
|
} |
|
774
|
|
|
|
|
775
|
|
|
} else { |
|
776
|
|
|
throw new ActiveRecordException("Relation $name not found."); |
|
777
|
|
|
} |
|
778
|
|
|
|
|
779
|
2 |
|
return $this->relations[$name]; |
|
780
|
|
|
} |
|
781
|
|
|
|
|
782
|
|
|
/** |
|
783
|
|
|
* Helper function to build SQL with sql parts. |
|
784
|
|
|
* |
|
785
|
|
|
* @param string $sql_string_part <p>The SQL part will be build.</p> |
|
786
|
|
|
* @param int $index <p>The index of $n in $sql array.</p> |
|
787
|
|
|
* @param self $active_record <p>The reference to $this.</p> |
|
788
|
|
|
*/ |
|
789
|
21 |
|
private function _buildSqlCallback(string &$sql_string_part, $index, self $active_record) |
|
790
|
|
|
{ |
|
791
|
|
|
if ( |
|
792
|
21 |
|
'select' === $sql_string_part |
|
793
|
|
|
&& |
|
794
|
21 |
|
null === $active_record->{$sql_string_part} |
|
795
|
|
|
) { |
|
796
|
|
|
|
|
797
|
14 |
|
$sql_string_part = \strtoupper($sql_string_part) . ' ' . $active_record->table . '.*'; |
|
798
|
|
|
|
|
799
|
|
|
} elseif ( |
|
800
|
|
|
( |
|
801
|
21 |
|
'update' === $sql_string_part |
|
802
|
|
|
|| |
|
803
|
21 |
|
'from' === $sql_string_part |
|
804
|
|
|
) |
|
805
|
|
|
&& |
|
806
|
21 |
|
null === $active_record->{$sql_string_part} |
|
807
|
|
|
) { |
|
808
|
|
|
|
|
809
|
17 |
|
$sql_string_part = \strtoupper($sql_string_part) . ' ' . $active_record->table; |
|
810
|
|
|
|
|
811
|
21 |
|
} elseif ('delete' === $sql_string_part) { |
|
812
|
|
|
|
|
813
|
1 |
|
$sql_string_part = \strtoupper($sql_string_part) . ' '; |
|
814
|
|
|
|
|
815
|
|
|
} else { |
|
816
|
|
|
|
|
817
|
21 |
|
$sql_string_part = (null !== $active_record->{$sql_string_part}) ? $active_record->{$sql_string_part} . ' ' : ''; |
|
818
|
|
|
|
|
819
|
|
|
} |
|
820
|
21 |
|
} |
|
821
|
|
|
|
|
822
|
|
|
/** |
|
823
|
|
|
* Helper function to build SQL with sql parts. |
|
824
|
|
|
* |
|
825
|
|
|
* @param string[] $sql_array <p>The SQL part will be build.</p> |
|
826
|
|
|
* |
|
827
|
|
|
* @return string |
|
828
|
|
|
*/ |
|
829
|
21 |
|
protected function _buildSql(array $sql_array = []): string |
|
830
|
|
|
{ |
|
831
|
21 |
|
\array_walk($sql_array, [$this, '_buildSqlCallback'], $this); |
|
832
|
|
|
|
|
833
|
|
|
// DEBUG |
|
834
|
|
|
//echo 'SQL: ', implode(' ', $sqls), "\n", 'PARAMS: ', implode(', ', $this->params), "\n"; |
|
835
|
|
|
|
|
836
|
21 |
|
return \implode(' ', $sql_array); |
|
837
|
|
|
} |
|
838
|
|
|
|
|
839
|
|
|
/** |
|
840
|
|
|
* Magic function to make calls witch in function mapping stored in $operators and $sqlPart. |
|
841
|
|
|
* also can call function of DB object. |
|
842
|
|
|
* |
|
843
|
|
|
* @param string $name <p>The name of the function.</p> |
|
844
|
|
|
* @param array $args <p>The arguments of the function.</p> |
|
845
|
|
|
* |
|
846
|
|
|
* @return $this|mixed <p>Return the result of callback or the current object to make chain method calls.</p> |
|
847
|
|
|
* |
|
848
|
|
|
* @throws ActiveRecordException |
|
849
|
|
|
*/ |
|
850
|
16 |
|
public function __call(string $name, array $args = []) |
|
851
|
|
|
{ |
|
852
|
16 |
|
if (!self::$db instanceof DB) { |
|
853
|
|
|
self::$db = DB::getInstance(); |
|
|
|
|
|
|
854
|
|
|
} |
|
855
|
|
|
|
|
856
|
16 |
|
$nameTmp = \strtolower($name); |
|
857
|
|
|
|
|
858
|
16 |
|
if (\array_key_exists($nameTmp, self::$operators)) { |
|
859
|
|
|
|
|
860
|
14 |
|
$this->addCondition( |
|
861
|
14 |
|
$args[0], |
|
862
|
14 |
|
self::$operators[$nameTmp], |
|
863
|
14 |
|
$args[1] ?? null, |
|
864
|
14 |
|
(\is_string(\end($args)) && 'or' === \strtolower(\end($args))) ? 'OR' : 'AND' |
|
865
|
|
|
); |
|
866
|
|
|
|
|
867
|
11 |
|
} elseif (\array_key_exists($nameTmp = \str_replace('by', '', $nameTmp), $this->sqlParts)) { |
|
868
|
|
|
|
|
869
|
11 |
|
$this->{$name} = new ActiveRecordExpressions( |
|
870
|
|
|
[ |
|
871
|
11 |
|
'operator' => $this->sqlParts[$nameTmp], |
|
872
|
11 |
|
'target' => \implode(', ', $args), |
|
873
|
|
|
] |
|
874
|
|
|
); |
|
875
|
|
|
|
|
876
|
|
|
} elseif (\is_callable($callback = [self::$db, $name])) { |
|
877
|
|
|
|
|
878
|
|
|
return \call_user_func_array($callback, $args); |
|
879
|
|
|
|
|
880
|
|
|
} else { |
|
881
|
|
|
|
|
882
|
|
|
throw new ActiveRecordException("Method $name not exist."); |
|
883
|
|
|
|
|
884
|
|
|
} |
|
885
|
|
|
|
|
886
|
16 |
|
return $this; |
|
887
|
|
|
} |
|
888
|
|
|
|
|
889
|
|
|
/** |
|
890
|
|
|
* Make wrap when build the SQL expressions of WHERE. |
|
891
|
|
|
* |
|
892
|
|
|
* @param string $op <p>If given, this param will build one "ActiveRecordExpressionsWrap" and include the stored |
|
893
|
|
|
* expressions add into WHERE, otherwise it will stored the expressions into an array.</p> |
|
894
|
|
|
* |
|
895
|
|
|
* @return $this |
|
896
|
|
|
*/ |
|
897
|
1 |
|
public function wrap($op = null) |
|
898
|
|
|
{ |
|
899
|
1 |
|
if (1 === \func_num_args()) { |
|
900
|
1 |
|
$this->wrap = false; |
|
|
|
|
|
|
901
|
1 |
|
if (\is_array($this->expressions) && \count($this->expressions) > 0) { |
|
|
|
|
|
|
902
|
1 |
|
$this->_addCondition( |
|
903
|
1 |
|
new ActiveRecordExpressionsWrap( |
|
904
|
|
|
[ |
|
905
|
1 |
|
'delimiter' => ' ', |
|
906
|
1 |
|
'target' => $this->expressions, |
|
|
|
|
|
|
907
|
|
|
] |
|
908
|
|
|
), |
|
909
|
1 |
|
'or' === \strtolower($op) ? 'OR' : 'AND' |
|
910
|
|
|
); |
|
911
|
|
|
} |
|
912
|
1 |
|
$this->expressions = []; |
|
|
|
|
|
|
913
|
|
|
} else { |
|
914
|
1 |
|
$this->wrap = true; |
|
|
|
|
|
|
915
|
|
|
} |
|
916
|
|
|
|
|
917
|
1 |
|
return $this; |
|
918
|
|
|
} |
|
919
|
|
|
|
|
920
|
|
|
/** |
|
921
|
|
|
* Helper function to build place holder when make SQL expressions. |
|
922
|
|
|
* |
|
923
|
|
|
* @param mixed $value <p>The value will be bind to SQL, just store it in $this->params.</p> |
|
924
|
|
|
* |
|
925
|
|
|
* @return mixed $value |
|
926
|
|
|
*/ |
|
927
|
18 |
|
protected function _filterParam($value) |
|
928
|
|
|
{ |
|
929
|
18 |
|
if (\is_array($value)) { |
|
930
|
8 |
|
foreach ($value as $key => $val) { |
|
931
|
8 |
|
$this->params[$value[$key] = self::PREFIX . ++self::$count] = $val; |
|
932
|
|
|
} |
|
933
|
11 |
|
} elseif (\is_string($value)) { |
|
934
|
3 |
|
$this->params[$ph = self::PREFIX . ++self::$count] = $value; |
|
935
|
3 |
|
$value = $ph; |
|
936
|
|
|
} |
|
937
|
|
|
|
|
938
|
18 |
|
return $value; |
|
939
|
|
|
} |
|
940
|
|
|
|
|
941
|
|
|
/** |
|
942
|
|
|
* Helper function to add condition into WHERE. |
|
943
|
|
|
* |
|
944
|
|
|
* @param string $field <p>The field name, the source of Expressions</p> |
|
945
|
|
|
* @param string $operator <p>The operator for this condition.</p> |
|
946
|
|
|
* @param mixed $value <p>The target of the Expressions.</p> |
|
947
|
|
|
* @param string $operator_concat <p>The operator to concat this Expressions into WHERE or SET statement.</p> |
|
948
|
|
|
* @param string $name <p>The Expression will contact to.</p> |
|
949
|
|
|
*/ |
|
950
|
14 |
|
public function addCondition($field, $operator, $value, $operator_concat = 'AND', $name = 'where') |
|
951
|
|
|
{ |
|
952
|
14 |
|
$value = $this->_filterParam($value); |
|
953
|
14 |
|
$expression = new ActiveRecordExpressions( |
|
954
|
|
|
[ |
|
955
|
14 |
|
'source' => ('where' === strtolower($name) ? $this->table . '.' : '') . $field, |
|
956
|
14 |
|
'operator' => $operator, |
|
957
|
14 |
|
'target' => \is_array($value) |
|
958
|
4 |
|
? new ActiveRecordExpressionsWrap( |
|
959
|
4 |
|
'between' === \strtolower($operator) |
|
960
|
1 |
|
? ['target' => $value, 'start' => ' ', 'end' => ' ', 'delimiter' => ' AND '] |
|
961
|
4 |
|
: ['target' => $value] |
|
962
|
14 |
|
) : $value, |
|
963
|
|
|
] |
|
964
|
|
|
); |
|
965
|
|
|
|
|
966
|
14 |
|
if ($expression) { |
|
967
|
14 |
|
if (!$this->wrap) { |
|
|
|
|
|
|
968
|
14 |
|
$this->_addCondition($expression, $operator_concat, $name); |
|
969
|
|
|
} else { |
|
970
|
1 |
|
$this->_addExpression($expression, $operator_concat); |
|
971
|
|
|
} |
|
972
|
|
|
} |
|
973
|
14 |
|
} |
|
974
|
|
|
|
|
975
|
|
|
/** |
|
976
|
|
|
* Helper function to add condition into JOIN. |
|
977
|
|
|
* |
|
978
|
|
|
* @param string $table <p>The join table name.</p> |
|
979
|
|
|
* @param string $on <p>The condition of ON.</p> |
|
980
|
|
|
* @param string $type <p>The join type, like "LEFT", "INNER", "OUTER".</p> |
|
981
|
|
|
* |
|
982
|
|
|
* @return $this |
|
983
|
|
|
*/ |
|
984
|
1 |
|
public function join($table, $on, $type = 'LEFT') |
|
985
|
|
|
{ |
|
986
|
1 |
|
$this->join = new ActiveRecordExpressions( |
|
|
|
|
|
|
987
|
|
|
[ |
|
988
|
1 |
|
'source' => $this->join ?: '', |
|
|
|
|
|
|
989
|
1 |
|
'operator' => $type . ' JOIN', |
|
990
|
1 |
|
'target' => new ActiveRecordExpressions( |
|
991
|
1 |
|
['source' => $table, 'operator' => 'ON', 'target' => $on] |
|
992
|
|
|
), |
|
993
|
|
|
] |
|
994
|
|
|
); |
|
995
|
|
|
|
|
996
|
1 |
|
return $this; |
|
997
|
|
|
} |
|
998
|
|
|
|
|
999
|
|
|
/** |
|
1000
|
|
|
* helper function to make wrapper. Stored the expression in to array. |
|
1001
|
|
|
* |
|
1002
|
|
|
* @param ActiveRecordExpressions $exp <p>The expression will be stored.</p> |
|
1003
|
|
|
* @param string $operator <p>The operator to concat this Expressions into WHERE statement.</p> |
|
1004
|
|
|
*/ |
|
1005
|
1 |
|
protected function _addExpression($exp, $operator) |
|
1006
|
|
|
{ |
|
1007
|
|
|
if ( |
|
1008
|
1 |
|
!\is_array($this->expressions) |
|
|
|
|
|
|
1009
|
|
|
|| |
|
1010
|
1 |
|
\count($this->expressions) === 0 |
|
|
|
|
|
|
1011
|
|
|
) { |
|
1012
|
1 |
|
$this->expressions = [$exp]; |
|
|
|
|
|
|
1013
|
|
|
} else { |
|
1014
|
1 |
|
$this->expressions[] = new ActiveRecordExpressions(['operator' => $operator, 'target' => $exp]); |
|
|
|
|
|
|
1015
|
|
|
} |
|
1016
|
1 |
|
} |
|
1017
|
|
|
|
|
1018
|
|
|
/** |
|
1019
|
|
|
* helper function to add condition into WHERE. |
|
1020
|
|
|
* |
|
1021
|
|
|
* @param ActiveRecordExpressions $expression <p>The expression will be concat into WHERE or SET statement.</p> |
|
1022
|
|
|
* @param string $operator <p>The operator to concat this Expressions into WHERE or SET |
|
1023
|
|
|
* statement.</p> |
|
1024
|
|
|
* @param string $name <p>The Expression will contact to.</p> |
|
1025
|
|
|
*/ |
|
1026
|
14 |
|
protected function _addCondition($expression, $operator, $name = 'where') |
|
1027
|
|
|
{ |
|
1028
|
14 |
|
if (!$this->{$name}) { |
|
1029
|
|
|
|
|
1030
|
14 |
|
$this->{$name} = new ActiveRecordExpressions( |
|
1031
|
|
|
[ |
|
1032
|
14 |
|
'operator' => \strtoupper($name), |
|
1033
|
14 |
|
'target' => $expression, |
|
1034
|
|
|
] |
|
1035
|
|
|
); |
|
1036
|
|
|
|
|
1037
|
|
|
} else { |
|
1038
|
|
|
|
|
1039
|
4 |
|
$this->{$name}->target = new ActiveRecordExpressions( |
|
1040
|
|
|
[ |
|
1041
|
4 |
|
'source' => $this->{$name}->target, |
|
1042
|
4 |
|
'operator' => $operator, |
|
1043
|
4 |
|
'target' => $expression, |
|
1044
|
|
|
] |
|
1045
|
|
|
); |
|
1046
|
|
|
|
|
1047
|
|
|
} |
|
1048
|
14 |
|
} |
|
1049
|
|
|
|
|
1050
|
|
|
/** |
|
1051
|
|
|
* @return array |
|
1052
|
|
|
*/ |
|
1053
|
1 |
|
public function getDirty(): array |
|
1054
|
|
|
{ |
|
1055
|
1 |
|
return $this->dirty; |
|
1056
|
|
|
} |
|
1057
|
|
|
|
|
1058
|
|
|
/** |
|
1059
|
|
|
* @return bool |
|
1060
|
|
|
*/ |
|
1061
|
|
|
public static function isNewDataAreDirty(): bool |
|
1062
|
|
|
{ |
|
1063
|
|
|
return self::$new_data_are_dirty; |
|
1064
|
|
|
} |
|
1065
|
|
|
|
|
1066
|
|
|
/** |
|
1067
|
|
|
* @param bool $bool |
|
1068
|
|
|
*/ |
|
1069
|
17 |
|
public static function setNewDataAreDirty($bool) |
|
1070
|
|
|
{ |
|
1071
|
17 |
|
self::$new_data_are_dirty = (bool)$bool; |
|
1072
|
17 |
|
} |
|
1073
|
|
|
|
|
1074
|
|
|
/** |
|
1075
|
|
|
* Magic function to SET values of the current object. |
|
1076
|
|
|
* |
|
1077
|
|
|
* @param mixed $var |
|
1078
|
|
|
* @param mixed $val |
|
1079
|
|
|
*/ |
|
1080
|
23 |
|
public function __set($var, $val) |
|
1081
|
|
|
{ |
|
1082
|
|
|
if ( |
|
1083
|
23 |
|
\array_key_exists($var, $this->sqlExpressions) |
|
1084
|
|
|
|| |
|
1085
|
23 |
|
\array_key_exists($var, $this->defaultSqlExpressions) |
|
1086
|
|
|
) { |
|
1087
|
|
|
|
|
1088
|
20 |
|
$this->sqlExpressions[$var] = $val; |
|
1089
|
|
|
|
|
1090
|
|
|
} elseif ( |
|
1091
|
20 |
|
\array_key_exists($var, $this->relations) |
|
1092
|
|
|
&& |
|
1093
|
20 |
|
$val instanceof self |
|
1094
|
|
|
) { |
|
1095
|
|
|
|
|
1096
|
1 |
|
$this->relations[$var] = $val; |
|
1097
|
|
|
|
|
1098
|
|
|
} else { |
|
1099
|
|
|
|
|
1100
|
20 |
|
$this->array[$var] = $val; |
|
1101
|
|
|
|
|
1102
|
20 |
|
if (self::$new_data_are_dirty === true) { |
|
1103
|
9 |
|
$this->dirty[$var] = $val; |
|
1104
|
|
|
} |
|
1105
|
|
|
|
|
1106
|
|
|
} |
|
1107
|
23 |
|
} |
|
1108
|
|
|
|
|
1109
|
|
|
/** |
|
1110
|
|
|
* Magic function to UNSET values of the current object. |
|
1111
|
|
|
* |
|
1112
|
|
|
* @param mixed $var |
|
1113
|
|
|
*/ |
|
1114
|
1 |
|
public function __unset($var) |
|
1115
|
|
|
{ |
|
1116
|
1 |
|
if (\array_key_exists($var, $this->sqlExpressions)) { |
|
1117
|
|
|
unset($this->sqlExpressions[$var]); |
|
1118
|
|
|
} |
|
1119
|
|
|
|
|
1120
|
1 |
|
if (isset($this->array[$var])) { |
|
1121
|
1 |
|
unset($this->array[$var]); |
|
1122
|
|
|
} |
|
1123
|
|
|
|
|
1124
|
1 |
|
if (isset($this->dirty[$var])) { |
|
1125
|
1 |
|
unset($this->dirty[$var]); |
|
1126
|
|
|
} |
|
1127
|
1 |
|
} |
|
1128
|
|
|
|
|
1129
|
|
|
/** |
|
1130
|
|
|
* Helper function for "GROUP BY". |
|
1131
|
|
|
* |
|
1132
|
|
|
* @param array $args |
|
1133
|
|
|
* |
|
1134
|
|
|
* @return $this |
|
1135
|
|
|
*/ |
|
1136
|
|
|
public function groupBy($args) |
|
|
|
|
|
|
1137
|
|
|
{ |
|
1138
|
|
|
$this->__call('groupBy', \func_get_args()); |
|
1139
|
|
|
|
|
1140
|
|
|
return $this; |
|
1141
|
|
|
} |
|
1142
|
|
|
|
|
1143
|
|
|
/** |
|
1144
|
|
|
* Helper function for "ORDER BY". |
|
1145
|
|
|
* |
|
1146
|
|
|
* @param $args ... |
|
1147
|
|
|
* |
|
1148
|
|
|
* @return $this |
|
1149
|
|
|
*/ |
|
1150
|
2 |
|
public function orderBy($args) |
|
|
|
|
|
|
1151
|
|
|
{ |
|
1152
|
2 |
|
$this->__call('orderBy', \func_get_args()); |
|
1153
|
|
|
|
|
1154
|
2 |
|
return $this; |
|
1155
|
|
|
} |
|
1156
|
|
|
|
|
1157
|
|
|
/** |
|
1158
|
|
|
* Magic function to GET the values of current object. |
|
1159
|
|
|
* |
|
1160
|
|
|
* @param $var |
|
1161
|
|
|
* |
|
1162
|
|
|
* @return mixed |
|
1163
|
|
|
*/ |
|
1164
|
23 |
|
public function &__get($var) |
|
1165
|
|
|
{ |
|
1166
|
23 |
|
if (\array_key_exists($var, $this->sqlExpressions)) { |
|
1167
|
20 |
|
return $this->sqlExpressions[$var]; |
|
1168
|
|
|
} |
|
1169
|
|
|
|
|
1170
|
23 |
|
if (\array_key_exists($var, $this->relations)) { |
|
1171
|
3 |
|
return $this->getRelation($var); |
|
|
|
|
|
|
1172
|
|
|
} |
|
1173
|
|
|
|
|
1174
|
23 |
|
if (isset($this->dirty[$var])) { |
|
1175
|
4 |
|
return $this->dirty[$var]; |
|
1176
|
|
|
} |
|
1177
|
|
|
|
|
1178
|
23 |
|
return parent::__get($var); |
|
1179
|
|
|
} |
|
1180
|
|
|
} |
|
1181
|
|
|
|
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.