1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Cozy\Database; |
4
|
|
|
|
5
|
|
|
class Statement |
6
|
|
|
{ |
7
|
|
|
/** @var \PDOStatement */ |
8
|
|
|
protected $pdoStatement; |
9
|
|
|
protected $wasExecuted = false; |
10
|
|
|
protected $autoExecute = true; |
11
|
|
|
protected $paramsMap = []; // ['param_name' => 'type'] |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Statement constructor that wraps a PDOStatement object. |
15
|
|
|
* |
16
|
|
|
* @param \PDOStatement $pdoStatement |
17
|
|
|
*/ |
18
|
|
|
public function __construct(\PDOStatement $pdoStatement) |
19
|
|
|
{ |
20
|
|
|
$this->pdoStatement = $pdoStatement; |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Returns the wrapped PDO statement object. |
25
|
|
|
* |
26
|
|
|
* @return \PDOStatement |
27
|
|
|
*/ |
28
|
|
|
public function getPdoStatement() |
29
|
|
|
{ |
30
|
|
|
return $this->pdoStatement; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Returns error information about the last operation performed by this statement. |
35
|
|
|
* |
36
|
|
|
* @return array |
37
|
|
|
*/ |
38
|
|
|
public function getErrorInfo() |
39
|
|
|
{ |
40
|
|
|
return $this->pdoStatement->errorInfo(); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Map out the parameters of this statement. |
45
|
|
|
* |
46
|
|
|
* @param array $params A key pair array where the key is parameter's name and the value is parameter's type. |
47
|
|
|
* @return $this |
48
|
|
|
*/ |
49
|
|
|
public function mapParams(array $params) |
50
|
|
|
{ |
51
|
|
|
$this->paramsMap = []; |
52
|
|
|
|
53
|
|
|
foreach ($params as $name => $type) { |
54
|
|
|
// The parameters with named placeholders must start with character ':' |
55
|
|
|
if (is_string($name) && strpos($name, ':') !== 0) { |
56
|
|
|
$name = ':' . $name; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
// The parameters with positional ? placeholders must start with number 1 |
60
|
|
|
if (array_key_exists(0, $params)) { |
61
|
|
|
$name++; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
if ($type === 'int' || $type === 'integer' || $type === \PDO::PARAM_INT) { |
65
|
|
|
$this->paramsMap[$name] = \PDO::PARAM_INT; |
66
|
|
|
} elseif ($type === 'bool' || $type === 'boolean' || $type === \PDO::PARAM_BOOL) { |
67
|
|
|
$this->paramsMap[$name] = \PDO::PARAM_BOOL; |
68
|
|
|
} elseif ($type === 'lob' || $type === 'blob' || $type === \PDO::PARAM_LOB) { |
69
|
|
|
$this->paramsMap[$name] = \PDO::PARAM_LOB; |
70
|
|
|
} elseif ($type === 'null' || $type === \PDO::PARAM_NULL) { |
71
|
|
|
$this->paramsMap[$name] = \PDO::PARAM_NULL; |
72
|
|
|
} else { |
73
|
|
|
$this->paramsMap[$name] = \PDO::PARAM_STR; |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
return $this; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Binds values to mapped parameters. |
82
|
|
|
* |
83
|
|
|
* @param array $values A key pair array where the key is parameter's name. |
84
|
|
|
* @return $this |
85
|
|
|
*/ |
86
|
|
|
public function bindValues(array $values) |
87
|
|
|
{ |
88
|
|
|
// Validations |
89
|
|
|
|
90
|
|
|
if (!$this->paramsMap) { |
|
|
|
|
91
|
|
|
throw new \RuntimeException('No mapped parameters found.'); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
if (!$values) { |
|
|
|
|
95
|
|
|
throw new \InvalidArgumentException('No values were passed.'); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
foreach ($values as $name => $value) { |
99
|
|
|
// The parameters with named placeholders must start with character ':' |
100
|
|
|
if (is_string($name) && strpos($name, ':') !== 0) { |
101
|
|
|
$name = ':' . $name; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// The parameters with positional ? placeholders must start with number 1 |
105
|
|
|
if (array_key_exists(0, $values)) { |
106
|
|
|
$name++; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
if (!isset($this->paramsMap[$name])) { |
110
|
|
|
throw new \InvalidArgumentException("The parameter [{$name}] was not previously mapped."); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
if ($value === null) { |
114
|
|
|
if (!$this->pdoStatement->bindValue($name, null, \PDO::PARAM_NULL)) { |
115
|
|
|
throw new \RuntimeException("Error binding the parameter [{$name}]."); |
116
|
|
|
} |
117
|
|
|
} else { |
118
|
|
|
if (!$this->pdoStatement->bindValue($name, $value, $this->paramsMap[$name])) { |
119
|
|
|
throw new \RuntimeException("Error binding the parameter [{$name}]."); |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
return $this; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Binds a value to a parameter. |
129
|
|
|
* |
130
|
|
|
* @param mixed $parameter Parameter identifier. |
131
|
|
|
* @param mixed $value The value to bind to the parameter. |
132
|
|
|
* @param string $data_type [optional] Explicit data type for the parameter. |
133
|
|
|
* @return $this |
134
|
|
|
*/ |
135
|
|
|
public function bindValue($parameter, $value, string $data_type = 'str') |
136
|
|
|
{ |
137
|
|
|
if ($value === null) { |
138
|
|
|
if (!$this->pdoStatement->bindValue($parameter, null, \PDO::PARAM_NULL)) { |
139
|
|
|
throw new \RuntimeException("Error binding the parameter [{$parameter}]."); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
$type = \PDO::PARAM_STR; |
144
|
|
|
|
145
|
|
|
if ($data_type === 'int' || $data_type === 'integer' || $data_type === \PDO::PARAM_INT) { |
146
|
|
|
$type = \PDO::PARAM_INT; |
147
|
|
|
} elseif ($data_type === 'bool' || $data_type === 'boolean' || $data_type === \PDO::PARAM_BOOL) { |
148
|
|
|
$type = \PDO::PARAM_BOOL; |
149
|
|
|
} elseif ($data_type === 'lob' || $data_type === 'blob' || $data_type === \PDO::PARAM_LOB) { |
150
|
|
|
$type = \PDO::PARAM_LOB; |
151
|
|
|
} elseif ($data_type === 'null' || $data_type === \PDO::PARAM_NULL) { |
152
|
|
|
$type = \PDO::PARAM_NULL; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if (!$this->pdoStatement->bindValue($parameter, $value, $type)) { |
156
|
|
|
throw new \RuntimeException("Error binding the parameter [{$parameter}]."); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return $this; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Define if the statement will execute automatically when trying to fetch data. |
164
|
|
|
* |
165
|
|
|
* @param bool $flag |
166
|
|
|
* @return $this |
167
|
|
|
*/ |
168
|
|
|
public function setAutoExecute(bool $flag) |
169
|
|
|
{ |
170
|
|
|
$this->autoExecute = $flag; |
171
|
|
|
|
172
|
|
|
return $this; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Execute the statement and return the number of rows that were modified or deleted. |
177
|
|
|
* If no rows were affected, this method returns 0. This method may return Boolean FALSE, but may also return a |
178
|
|
|
* non-Boolean value which evaluates to FALSE, so use the === operator for testing the return value of this method. |
179
|
|
|
* |
180
|
|
|
* @param array|null $valuesToBind A key pair array where the key is parameter's name. |
181
|
|
|
* @return int|false |
182
|
|
|
*/ |
183
|
|
|
public function execute(array $valuesToBind = null) |
184
|
|
|
{ |
185
|
|
|
if ($valuesToBind) { |
186
|
|
|
$this->bindValues($valuesToBind); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$this->wasExecuted = true; |
190
|
|
|
|
191
|
|
|
if ($this->pdoStatement->execute()) { |
192
|
|
|
return $this->pdoStatement->rowCount(); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
return false; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Returns the number of rows affected by the last SQL statement. |
200
|
|
|
* If there is no result set, returns 0. |
201
|
|
|
* |
202
|
|
|
* @return int |
203
|
|
|
*/ |
204
|
|
|
public function getRowCount(): int |
205
|
|
|
{ |
206
|
|
|
return $this->pdoStatement->rowCount(); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Returns the number of columns in the result set. |
211
|
|
|
* If there is no result set, returns 0. |
212
|
|
|
* |
213
|
|
|
* @return int |
214
|
|
|
*/ |
215
|
|
|
public function getColumnCount(): int |
216
|
|
|
{ |
217
|
|
|
return $this->pdoStatement->columnCount(); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Returns metadata for a column in a result set. |
222
|
|
|
* Returns FALSE if the requested column does not exist in the result set, or if no result set exists. |
223
|
|
|
* |
224
|
|
|
* @param int $columnNumber |
225
|
|
|
* @return array|false |
226
|
|
|
*/ |
227
|
|
|
public function getColumnMeta(int $columnNumber) |
228
|
|
|
{ |
229
|
|
|
// Validations |
230
|
|
|
|
231
|
|
|
if ($columnNumber < 0) { |
232
|
|
|
throw new \InvalidArgumentException('The argument $columnNumber is not a valid integer.'); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
return $this->pdoStatement->getColumnMeta($columnNumber); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
// CUSTOM FETCH METHODS |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Fetches the first row from the result set and returns it as an associative array. |
242
|
|
|
* |
243
|
|
|
* @return bool|array |
244
|
|
|
*/ |
245
|
|
|
public function fetchFirstAsArray() |
246
|
|
|
{ |
247
|
|
|
// Auto execute block |
248
|
|
|
|
249
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
250
|
|
|
if ($this->execute() === false) { |
251
|
|
|
return false; |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
// Fetch the first row |
256
|
|
|
|
257
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
258
|
|
|
|
259
|
|
|
// Clear state and return result |
260
|
|
|
|
261
|
|
|
$this->pdoStatement->closeCursor(); |
262
|
|
|
|
263
|
|
|
if ($row === false && $this->pdoStatement->errorCode() === '00000') { |
264
|
|
|
return null; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
return $row; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Fetches the first row from the result set and returns it as an object. |
272
|
|
|
* |
273
|
|
|
* @param string $className Name of the created class. |
274
|
|
|
* @param array|null $arguments Elements of this array are passed to the constructor. |
275
|
|
|
* @return bool|object |
276
|
|
|
*/ |
277
|
|
|
public function fetchFirstAsObject($className = 'stdClass', array $arguments = null) |
278
|
|
|
{ |
279
|
|
|
// Validations |
280
|
|
|
|
281
|
|
|
if (!is_string($className) || $className == '') { |
282
|
|
|
throw new \InvalidArgumentException('The argument $className is not a valid string.'); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
// Auto execute block |
286
|
|
|
|
287
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
288
|
|
|
if ($this->execute() === false) { |
289
|
|
|
return false; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
// Fetch the first row as object |
294
|
|
|
|
295
|
|
|
$row = $this->pdoStatement->fetchObject($className, $arguments); |
|
|
|
|
296
|
|
|
|
297
|
|
|
// Clear state and return result |
298
|
|
|
|
299
|
|
|
$this->pdoStatement->closeCursor(); |
300
|
|
|
|
301
|
|
|
if ($row === false && $this->pdoStatement->errorCode() === '00000') { |
302
|
|
|
return null; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
return $row; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Fetches the first row from the result set and updates an existing object, mapping the columns as named properties. |
310
|
|
|
* |
311
|
|
|
* @param string $object Object to update. |
312
|
|
|
* @return bool|object |
313
|
|
|
*/ |
314
|
|
|
public function fetchFirstIntoObject($object) |
315
|
|
|
{ |
316
|
|
|
// Validations |
317
|
|
|
|
318
|
|
|
if (!is_object($object)) { |
319
|
|
|
throw new \InvalidArgumentException('The argument $object is not a valid object.'); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
// Auto execute block |
323
|
|
|
|
324
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
325
|
|
|
if ($this->execute() === false) { |
326
|
|
|
return false; |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
// Fetch the first fow as object |
331
|
|
|
|
332
|
|
|
$this->pdoStatement->setFetchMode(\PDO::FETCH_INTO, $object); |
333
|
|
|
$row = $this->pdoStatement->fetch(); |
334
|
|
|
|
335
|
|
|
// Clear state and return result |
336
|
|
|
|
337
|
|
|
$this->pdoStatement->closeCursor(); |
338
|
|
|
|
339
|
|
|
if ($row === false && $this->pdoStatement->errorCode() === '00000') { |
340
|
|
|
return null; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
return $row; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Returns the value of a single column from the first row of the result set. |
348
|
|
|
* |
349
|
|
|
* @param string $column Name of column you wish to retrieve. |
350
|
|
|
* @return mixed |
351
|
|
|
*/ |
352
|
|
|
public function fetchFirstAsColumn($column) |
353
|
|
|
{ |
354
|
|
|
// Validations |
355
|
|
|
|
356
|
|
|
if (!is_string($column) || $column == '') { |
357
|
|
|
throw new \InvalidArgumentException('The argument $column is not a valid string.'); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
// Auto execute block |
361
|
|
|
|
362
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
363
|
|
|
if ($this->execute() === false) { |
364
|
|
|
return false; |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
// Fetch the first row |
369
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
370
|
|
|
|
371
|
|
|
// More validations |
372
|
|
|
|
373
|
|
|
if ($row === false) { |
374
|
|
|
$this->pdoStatement->closeCursor(); |
375
|
|
|
|
376
|
|
|
if ($this->pdoStatement->errorCode() === '00000') { |
377
|
|
|
return null; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
return false; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
if (!isset($row[$column])) { |
384
|
|
|
throw new \InvalidArgumentException('The column to fetch is not present in the result set.'); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
// Clear state and return result |
388
|
|
|
|
389
|
|
|
$this->pdoStatement->closeCursor(); |
390
|
|
|
|
391
|
|
|
return $row[$column]; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Returns an array containing values of a single column retrieved from the result set rows. |
396
|
|
|
* |
397
|
|
|
* @param string $column Name of column you wish to retrieve. |
398
|
|
|
* @param string $indexBy Name of the column you want to assign as a row key. |
399
|
|
|
* @return array|bool |
400
|
|
|
*/ |
401
|
|
|
public function fetchAllAsColumn($column, $indexBy = null) |
402
|
|
|
{ |
403
|
|
|
// Validations |
404
|
|
|
|
405
|
|
|
if (!is_string($column) || $column == '') { |
406
|
|
|
throw new \InvalidArgumentException('The argument $column is not a valid string.'); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
if (isset($indexBy) && (!is_string($indexBy) || $indexBy == '')) { |
410
|
|
|
throw new \InvalidArgumentException('The argument $indexBy is not a valid string.'); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
// Auto execute block |
414
|
|
|
|
415
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
416
|
|
|
if ($this->execute() === false) { |
417
|
|
|
return false; |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
// Set initial values |
422
|
|
|
|
423
|
|
|
$result = []; |
424
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
425
|
|
|
|
426
|
|
|
// More validations |
427
|
|
|
|
428
|
|
|
if ($row === false) { |
429
|
|
|
$this->pdoStatement->closeCursor(); |
430
|
|
|
|
431
|
|
|
if ($this->pdoStatement->errorCode() === '00000') { |
432
|
|
|
return null; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
return false; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
if (!isset($row[$column])) { |
439
|
|
|
throw new \InvalidArgumentException('The column to fetch is not present in the result set.'); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
if ($indexBy && !isset($row[$indexBy])) { |
|
|
|
|
443
|
|
|
throw new \InvalidArgumentException('The column to index by is not present in the result set.'); |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
// Traversing the remaining rows |
447
|
|
|
|
448
|
|
|
while ($row) { |
449
|
|
|
if ($indexBy) { |
|
|
|
|
450
|
|
|
$result[$row[$indexBy]] = $row[$column]; |
451
|
|
|
} else { |
452
|
|
|
$result[] = $row[$column]; |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
// Clear state and return result |
459
|
|
|
|
460
|
|
|
$this->pdoStatement->closeCursor(); |
461
|
|
|
|
462
|
|
|
return $result; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Returns an associative array containing all of the result set. |
467
|
|
|
* |
468
|
|
|
* @param string $indexBy Name of the column you want to assign as a row key. |
469
|
|
|
* @param string $groupBy Name of the columns with which you want to group the result. You can include max. 3 columns by separating them with commas. |
470
|
|
|
* @return array|bool |
471
|
|
|
*/ |
472
|
|
|
public function fetchAllAsArray($indexBy = null, $groupBy = null) |
473
|
|
|
{ |
474
|
|
|
// Validations |
475
|
|
|
|
476
|
|
|
if (isset($indexBy) && (!is_string($indexBy) || $indexBy == '')) { |
477
|
|
|
throw new \InvalidArgumentException('The argument $indexBy is not a valid string.'); |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
if (isset($groupBy) && (!is_string($groupBy) || $groupBy == '')) { |
481
|
|
|
throw new \InvalidArgumentException('The argument $groupBy is not a valid string.'); |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
// Auto execute block |
485
|
|
|
|
486
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
487
|
|
|
if ($this->execute() === false) { |
488
|
|
|
return false; |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
// Set initial values |
493
|
|
|
|
494
|
|
|
$result = []; |
495
|
|
|
$groupByCount = 0; |
496
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
497
|
|
|
|
498
|
|
|
// More validations |
499
|
|
|
|
500
|
|
|
if ($row === false) { |
501
|
|
|
$this->pdoStatement->closeCursor(); |
502
|
|
|
|
503
|
|
|
if ($this->pdoStatement->errorCode() === '00000') { |
504
|
|
|
return null; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
return false; |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
if ($indexBy && !isset($row[$indexBy])) { |
|
|
|
|
511
|
|
|
throw new \InvalidArgumentException('The column to index-by is not present in the result set.'); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
if ($groupBy) { |
|
|
|
|
515
|
|
|
$groupBy = explode(',', str_replace(' ', '', $groupBy)); |
516
|
|
|
$groupByCount = count($groupBy); |
517
|
|
|
|
518
|
|
|
if ($groupByCount > 3) { |
519
|
|
|
throw new \InvalidArgumentException('You have exceeded the limit of 3 columns to group-by.'); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
foreach ($groupBy as $column) { |
523
|
|
|
$columnErr = []; |
524
|
|
|
|
525
|
|
|
if (!isset($row[$column])) { |
526
|
|
|
$columnErr[] = $column; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
if ($columnErr) { |
530
|
|
|
throw new \InvalidArgumentException('Some columns to group-by (' . |
531
|
|
|
implode(', ', $columnErr) |
532
|
|
|
. ') are not present in the result set.'); |
533
|
|
|
} |
534
|
|
|
} |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
// Traversing the remaining rows |
538
|
|
|
|
539
|
|
|
while ($row) { |
540
|
|
|
if (!$indexBy && !$groupBy) { |
|
|
|
|
541
|
|
|
$result[] = $row; |
542
|
|
|
} elseif ($indexBy && !$groupBy) { |
|
|
|
|
543
|
|
|
$result[$row[$indexBy]] = $row; |
544
|
|
|
} elseif ($indexBy && $groupBy) { |
|
|
|
|
545
|
|
|
switch ($groupByCount) { |
546
|
|
|
case 3: |
547
|
|
|
$result[$row[$groupBy[0]]][$row[$groupBy[1]]][$row[$groupBy[2]]][$row[$indexBy]] = $row; |
548
|
|
|
break; |
549
|
|
|
case 2: |
550
|
|
|
$result[$row[$groupBy[0]]][$row[$groupBy[1]]][$row[$indexBy]] = $row; |
551
|
|
|
break; |
552
|
|
|
case 1: |
553
|
|
|
$result[$row[$groupBy[0]]][$row[$indexBy]] = $row; |
554
|
|
|
break; |
555
|
|
|
} |
556
|
|
|
} elseif (!$indexBy && $groupBy) { |
|
|
|
|
557
|
|
|
switch ($groupByCount) { |
558
|
|
|
case 3: |
559
|
|
|
$result[$row[$groupBy[0]]][$row[$groupBy[1]]][$row[$groupBy[2]]][] = $row; |
560
|
|
|
break; |
561
|
|
|
case 2: |
562
|
|
|
$result[$row[$groupBy[0]]][$row[$groupBy[1]]][] = $row; |
563
|
|
|
break; |
564
|
|
|
case 1: |
565
|
|
|
$result[$row[$groupBy[0]]][] = $row; |
566
|
|
|
break; |
567
|
|
|
} |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
$row = $this->pdoStatement->fetch(\PDO::FETCH_ASSOC); |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
// Clear state and return result |
574
|
|
|
|
575
|
|
|
$this->pdoStatement->closeCursor(); |
576
|
|
|
|
577
|
|
|
return $result; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Returns an array of objects containing all of the result set. |
582
|
|
|
* |
583
|
|
|
* @param string $className Name of the class you want to instantiate. |
584
|
|
|
* @param array $classArgs Elements of this array are passed to the constructor of the class instantiated. |
585
|
|
|
* @param string $indexBy Name of the column you want to assign as a row key. |
586
|
|
|
* @param string $groupBy Name of the columns with which you want to group the result. You can include max. 3 columns by separating them with commas. |
587
|
|
|
* @return array|bool |
588
|
|
|
*/ |
589
|
|
|
public function fetchAllAsObject($className = 'stdClass', array $classArgs = null, $indexBy = null, $groupBy = null) |
590
|
|
|
{ |
591
|
|
|
// Validations |
592
|
|
|
|
593
|
|
|
if (!is_string($className) || $className === '') { |
594
|
|
|
throw new \InvalidArgumentException('The argument $className is not a valid string.'); |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
if (isset($indexBy) && (!is_string($indexBy) || $indexBy == '')) { |
598
|
|
|
throw new \InvalidArgumentException('The argument $indexBy is not a valid string.'); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
if (isset($groupBy) && (!is_string($groupBy) || $groupBy == '')) { |
602
|
|
|
throw new \InvalidArgumentException('The argument $groupBy is not a valid string.'); |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
// Auto execute block |
606
|
|
|
|
607
|
|
|
if (!$this->wasExecuted || $this->autoExecute) { |
608
|
|
|
if ($this->execute() === false) { |
609
|
|
|
return false; |
610
|
|
|
} |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
// Set initial values |
614
|
|
|
|
615
|
|
|
$result = []; |
616
|
|
|
$groupByCount = 0; |
617
|
|
|
$row = $this->pdoStatement->fetchObject($className, (array)$classArgs); |
618
|
|
|
|
619
|
|
|
// More validations |
620
|
|
|
|
621
|
|
|
if ($row === false) { |
622
|
|
|
$this->pdoStatement->closeCursor(); |
623
|
|
|
|
624
|
|
|
if ($this->pdoStatement->errorCode() === '00000') { |
625
|
|
|
return null; |
626
|
|
|
} |
627
|
|
|
|
628
|
|
|
return false; |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
if ($indexBy && !property_exists($row, $indexBy)) { |
|
|
|
|
632
|
|
|
throw new \InvalidArgumentException('The column to index-by is not present in the result set.'); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
if ($groupBy) { |
|
|
|
|
636
|
|
|
$groupBy = explode(',', str_replace(' ', '', $groupBy)); |
637
|
|
|
$groupByCount = count($groupBy); |
638
|
|
|
|
639
|
|
|
if ($groupByCount > 3) { |
640
|
|
|
throw new \InvalidArgumentException('You have exceeded the limit of 3 columns to group-by.'); |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
foreach ($groupBy as $column) { |
644
|
|
|
$columnErr = []; |
645
|
|
|
|
646
|
|
|
if (!property_exists($row, $column)) { |
647
|
|
|
$columnErr[] = $column; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
if ($columnErr) { |
651
|
|
|
throw new \InvalidArgumentException('Some columns to group-by (' . |
652
|
|
|
implode(', ', $columnErr) |
653
|
|
|
. ') are not present in the result set.'); |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
// Traversing the remaining rows |
659
|
|
|
|
660
|
|
|
while ($row) { |
661
|
|
|
if (!$indexBy && !$groupBy) { |
|
|
|
|
662
|
|
|
$result[] = $row; |
663
|
|
|
} elseif ($indexBy && !$groupBy) { |
|
|
|
|
664
|
|
|
$result[$row->{$indexBy}] = $row; |
665
|
|
|
} elseif ($indexBy && $groupBy) { |
|
|
|
|
666
|
|
|
switch ($groupByCount) { |
667
|
|
|
case 3: |
668
|
|
|
$result[$row->{$groupBy[0]}][$row->{$groupBy[1]}][$row->{$groupBy[2]}][$row->{$indexBy}] = $row; |
669
|
|
|
break; |
670
|
|
|
case 2: |
671
|
|
|
$result[$row->{$groupBy[0]}][$row->{$groupBy[1]}][$row->{$indexBy}] = $row; |
672
|
|
|
break; |
673
|
|
|
case 1: |
674
|
|
|
$result[$row->{$groupBy[0]}][$row->{$indexBy}] = $row; |
675
|
|
|
break; |
676
|
|
|
} |
677
|
|
|
} elseif (!$indexBy && $groupBy) { |
|
|
|
|
678
|
|
|
switch ($groupByCount) { |
679
|
|
|
case 3: |
680
|
|
|
$result[$row->{$groupBy[0]}][$row->{$groupBy[1]}][$row->{$groupBy[2]}][] = $row; |
681
|
|
|
break; |
682
|
|
|
case 2: |
683
|
|
|
$result[$row->{$groupBy[0]}][$row->{$groupBy[1]}][] = $row; |
684
|
|
|
break; |
685
|
|
|
case 1: |
686
|
|
|
$result[$row->{$groupBy[0]}][] = $row; |
687
|
|
|
break; |
688
|
|
|
} |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
$row = $this->pdoStatement->fetchObject($className, (array)$classArgs); |
692
|
|
|
} |
693
|
|
|
|
694
|
|
|
// Clear state and return result |
695
|
|
|
|
696
|
|
|
$this->pdoStatement->closeCursor(); |
697
|
|
|
|
698
|
|
|
return $result; |
699
|
|
|
} |
700
|
|
|
} |
701
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.