1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Todd Burry <[email protected]> |
4
|
|
|
* @copyright 2009-2017 Vanilla Forums Inc. |
5
|
|
|
* @license MIT |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace Garden\Db; |
9
|
|
|
|
10
|
|
|
use PDO; |
11
|
|
|
|
12
|
|
|
trait DatasetTrait { |
13
|
|
|
/** |
14
|
|
|
* @var int |
15
|
|
|
*/ |
16
|
|
|
private $offset = 0; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var int |
20
|
|
|
*/ |
21
|
|
|
private $limit = 0; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var string[] |
25
|
|
|
*/ |
26
|
|
|
private $order; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Get the dataset array. |
30
|
|
|
* |
31
|
|
|
* @return array |
32
|
|
|
*/ |
33
|
|
|
abstract public function getData(); |
34
|
|
|
|
35
|
1 |
|
public function getPage() { |
36
|
1 |
|
if ($this->getLimit() === 0) { |
37
|
|
|
return 1; |
38
|
|
|
} |
39
|
1 |
|
$result = floor($this->getOffset() / (int)$this->getLimit()) + 1; |
40
|
|
|
// $result = intdiv($this->getOffset(), (int)$this->getLimit()) + 1; |
|
|
|
|
41
|
1 |
|
return (int)$result; |
42
|
|
|
} |
43
|
|
|
|
44
|
3 |
|
public function setPage($page) { |
45
|
3 |
|
if (!is_numeric($page) || $page < 0) { |
46
|
|
|
throw new \InvalidArgumentException("Invalid page '$page.'", 500); |
47
|
|
|
} |
48
|
|
|
|
49
|
3 |
|
$this->setOffset(($page - 1) * $this->getLimit()); |
50
|
3 |
|
return $this; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Retrieve an external iterator |
55
|
|
|
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php |
56
|
|
|
* @return Traversable Returns a generator of all rows. |
|
|
|
|
57
|
|
|
*/ |
58
|
4 |
|
public function getIterator() { |
59
|
4 |
|
return new \ArrayIterator($this->getData()); |
60
|
|
|
} |
61
|
|
|
|
62
|
10 |
|
public function fetchAll($mode = 0, ...$args) { |
63
|
10 |
|
if ($mode === 0) { |
64
|
1 |
|
return $this->getData(); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
switch ($mode) { |
68
|
9 |
|
case PDO::FETCH_COLUMN: |
69
|
3 |
|
$result = $this->fetchArrayColumn(reset($args) ?: 0); |
70
|
3 |
|
break; |
71
|
6 |
|
case PDO::FETCH_COLUMN | PDO::FETCH_GROUP; |
|
|
|
|
72
|
3 |
|
$result = $this->fetchArrayColumn(0, reset($args) ?: 0, true); |
73
|
3 |
|
break; |
74
|
3 |
|
case PDO::FETCH_KEY_PAIR: |
75
|
3 |
|
$result = $this->fetchArrayColumn(1, 0); |
76
|
3 |
|
break; |
77
|
|
|
case PDO::FETCH_UNIQUE: |
78
|
|
|
$result = $this->fetchArrayColumn(null, reset($args) ?: 0); |
79
|
|
|
break; |
80
|
|
|
case PDO::FETCH_OBJ: |
81
|
|
|
$result = array_map(function ($row) { |
82
|
|
|
return (object)$row; |
83
|
|
|
}, $this->getData()); |
84
|
|
|
break; |
85
|
|
|
case PDO::FETCH_ASSOC: |
86
|
|
|
$result = array_map(function ($row) { |
87
|
|
|
return (array)$row; |
88
|
|
|
}, $this->getData()); |
89
|
|
|
break; |
90
|
|
|
default: |
91
|
|
|
// Don't know what to do, return null. |
92
|
|
|
$result = null; |
93
|
|
|
} |
94
|
9 |
|
return $result; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Fetch the data and perform a quasi-{@link array_column()} operation on it. |
99
|
|
|
* |
100
|
|
|
* @param string|int|null $columnKey The key or ordinal of the value or **null** to return the entire row. |
101
|
|
|
* @param string|int|null $indexKey The key or ordinal of the index or **null** to not index the data. |
102
|
|
|
* @param bool $grouped If true the result will be grouped by {@link $indexKey} and each value will be an array of rows. |
103
|
|
|
* @return array Returns the array of results. |
104
|
|
|
*/ |
105
|
9 |
|
public function fetchArrayColumn($columnKey = null, $indexKey = null, $grouped = false) { |
106
|
9 |
|
$arr = $this->getData(); |
107
|
|
|
|
108
|
9 |
|
if (empty($arr)) { |
109
|
|
|
return []; |
110
|
|
|
} |
111
|
|
|
|
112
|
9 |
|
$firstRow = reset($arr); |
113
|
|
|
|
114
|
9 |
|
if (is_int($columnKey) || is_int($indexKey)) { |
115
|
9 |
|
$i = 0; |
116
|
9 |
|
foreach ($firstRow as $name => $value) { |
117
|
9 |
|
if ($i === $columnKey) { |
118
|
9 |
|
$columnKey = $name; |
119
|
9 |
|
} |
120
|
|
|
|
121
|
9 |
|
if ($i === $indexKey) { |
122
|
6 |
|
$indexKey = $name; |
123
|
6 |
|
} |
124
|
|
|
|
125
|
9 |
|
if (!(is_int($columnKey) || is_int($indexKey))) { |
126
|
9 |
|
break; |
127
|
|
|
} |
128
|
9 |
|
$i++; |
129
|
9 |
|
} |
130
|
9 |
|
} |
131
|
|
|
|
132
|
9 |
|
if (!$grouped && is_array($firstRow)) { |
133
|
2 |
|
return array_column($arr, $columnKey, $indexKey); |
134
|
|
|
} else { |
135
|
7 |
|
$result = []; |
136
|
|
|
|
137
|
7 |
|
foreach ($arr as $i => $row) { |
138
|
7 |
|
if (is_array($row) || $row instanceof \ArrayAccess ) { |
139
|
4 |
|
$value = $columnKey === null ? $row : $row[$columnKey]; |
140
|
4 |
|
$index = $indexKey === null ? $i : $row[$indexKey]; |
141
|
4 |
|
} else { |
142
|
3 |
|
$value = $columnKey === null ? $row : $row->$columnKey; |
143
|
3 |
|
$index = $indexKey === null ? $i : $row->$indexKey; |
144
|
|
|
} |
145
|
|
|
|
146
|
7 |
|
if ($grouped) { |
147
|
3 |
|
$result[$index][] = $value; |
148
|
3 |
|
} else { |
149
|
4 |
|
$result[$index] = $value; |
150
|
|
|
} |
151
|
7 |
|
} |
152
|
|
|
|
153
|
7 |
|
return $result; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Get the first row of data. |
159
|
|
|
* |
160
|
|
|
* @return mixed|null Returns the first row or **null** if there is no data. |
161
|
|
|
*/ |
162
|
1 |
|
public function firstRow() { |
163
|
1 |
|
$data = $this->getData(); |
164
|
|
|
|
165
|
1 |
|
return empty($data) ? null : $data[0]; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Get the number of records queried. |
170
|
|
|
* |
171
|
|
|
* @return int Returns the count. |
172
|
|
|
*/ |
173
|
11 |
|
public function count() { |
174
|
11 |
|
return count($this->getData()); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Specify data which should be serialized to JSON |
179
|
|
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php |
180
|
|
|
* @return mixed data which can be serialized by <b>json_encode</b>, |
181
|
|
|
* which is a value of any type other than a resource. |
182
|
|
|
* @since 5.4.0 |
183
|
|
|
*/ |
184
|
|
|
public function jsonSerialize() { |
185
|
|
|
return $this->getData(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Get the offset. |
190
|
|
|
* |
191
|
|
|
* @return int Returns the offset. |
192
|
|
|
*/ |
193
|
2 |
|
public function getOffset() { |
194
|
2 |
|
return $this->offset; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Set the offset. |
199
|
|
|
* |
200
|
|
|
* @param int $offset |
201
|
|
|
* @return $this |
202
|
|
|
*/ |
203
|
1 |
|
public function setOffset($offset) { |
204
|
1 |
|
if (!is_numeric($offset) || $offset < 0) { |
205
|
|
|
throw new \InvalidArgumentException("Invalid offset '$offset.'", 500); |
206
|
|
|
} |
207
|
|
|
|
208
|
1 |
|
$this->offset = (int)$offset; |
209
|
1 |
|
return $this; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Get the limit. |
214
|
|
|
* |
215
|
|
|
* @return int Returns the limit. |
216
|
|
|
*/ |
217
|
4 |
|
public function getLimit() { |
218
|
4 |
|
return $this->limit; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Set the limit. |
223
|
|
|
* |
224
|
|
|
* @param int $limit |
225
|
|
|
* @return $this |
226
|
|
|
*/ |
227
|
2 |
|
public function setLimit($limit) { |
228
|
2 |
|
if (!is_numeric($limit) || $limit < 0) { |
229
|
|
|
throw new \InvalidArgumentException("Invalid limit '$limit.'", 500); |
230
|
|
|
} |
231
|
|
|
|
232
|
2 |
|
$this->limit = (int)$limit; |
233
|
2 |
|
return $this; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Get the order. |
238
|
|
|
* |
239
|
|
|
* @return array Returns the order. |
240
|
|
|
*/ |
241
|
4 |
|
public function getOrder() { |
242
|
4 |
|
return $this->order; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Set the order. |
247
|
|
|
* |
248
|
|
|
* @param mixed $columns |
249
|
|
|
* @return $this |
250
|
|
|
*/ |
251
|
|
|
public function setOrder(...$columns) { |
252
|
|
|
$this->order = $columns; |
|
|
|
|
253
|
|
|
return $this; |
254
|
|
|
} |
255
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.