1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @copyright Copyright (c) 2015 ublaboo <[email protected]> |
5
|
|
|
* @author Pavel Janda <[email protected]> |
6
|
|
|
* @package Ublaboo |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Ublaboo\DataGrid\DataSource; |
10
|
|
|
|
11
|
|
|
use Ublaboo\DataGrid\Filter\Filter; |
12
|
|
|
use Ublaboo\DataGrid\Filter\FilterDate; |
13
|
|
|
use Ublaboo\DataGrid\Filter\FilterMultiSelect; |
14
|
|
|
use Ublaboo\DataGrid\Filter\FilterRange; |
15
|
|
|
use Ublaboo\DataGrid\Filter\FilterDateRange; |
16
|
|
|
use Nette\Utils\Callback; |
17
|
|
|
use Nette\Utils\Strings; |
18
|
|
|
use Ublaboo\DataGrid\Exception\DataGridException; |
19
|
|
|
use Ublaboo\DataGrid\Exception\DataGridArrayDataSourceException; |
20
|
|
|
use Ublaboo\DataGrid\Utils\DateTimeHelper; |
21
|
|
|
use Ublaboo\DataGrid\Exception\DataGridDateTimeHelperException; |
22
|
|
|
use Ublaboo\DataGrid\Utils\Sorting; |
23
|
|
|
|
24
|
|
|
class ArrayDataSource implements IDataSource |
25
|
|
|
{ |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $data = []; |
31
|
|
|
|
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @param array $data_source |
35
|
|
|
*/ |
36
|
|
|
public function __construct(array $data_source) |
37
|
|
|
{ |
38
|
|
|
$this->data = $data_source; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
|
42
|
|
|
/******************************************************************************** |
43
|
|
|
* IDataSource implementation * |
44
|
|
|
********************************************************************************/ |
45
|
|
|
|
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Get count of data |
49
|
|
|
* @return int |
50
|
|
|
*/ |
51
|
|
|
public function getCount() |
52
|
|
|
{ |
53
|
|
|
return sizeof($this->data); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Get the data |
59
|
|
|
* @return array |
60
|
|
|
* @return array |
61
|
|
|
*/ |
62
|
|
|
public function getData() |
63
|
|
|
{ |
64
|
|
|
return $this->data; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Filter data |
70
|
|
|
* @param Filter[] $filters |
71
|
|
|
* @return static |
72
|
|
|
*/ |
73
|
|
|
public function filter(array $filters) |
74
|
|
|
{ |
75
|
|
|
foreach ($filters as $filter) { |
76
|
|
|
if ($filter->isValueSet()) { |
77
|
|
|
if ($filter->hasConditionCallback()) { |
78
|
|
|
$this->data = (array) call_user_func_array( |
79
|
|
|
$filter->getConditionCallback(), |
80
|
|
|
[$this->data, $filter->getValue()] |
81
|
|
|
); |
82
|
|
|
} else { |
83
|
|
|
$this->data = array_filter($this->data, function($row) use ($filter) { |
84
|
|
|
return $this->applyFilter($row, $filter); |
85
|
|
|
}); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return $this; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Filter data - get one row |
96
|
|
|
* @param array $condition |
97
|
|
|
* @return ArrayDataSource |
98
|
|
|
*/ |
99
|
|
|
public function filterOne(array $condition) |
100
|
|
|
{ |
101
|
|
|
foreach ($this->data as $item) { |
102
|
|
|
foreach ($condition as $key => $value) { |
103
|
|
|
if ($item[$key] == $value) { |
104
|
|
|
$this->data = [$item]; |
105
|
|
|
|
106
|
|
|
return $this; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$this->data = []; |
112
|
|
|
|
113
|
|
|
return $this; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Apply limit and offset on data |
119
|
|
|
* @param int $offset |
120
|
|
|
* @param int $limit |
121
|
|
|
* @return static |
122
|
|
|
*/ |
123
|
|
|
public function limit($offset, $limit) |
124
|
|
|
{ |
125
|
|
|
$this->data = array_slice($this->data, $offset, $limit); |
126
|
|
|
|
127
|
|
|
return $this; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Apply fitler and tell whether row passes conditions or not |
133
|
|
|
* @param mixed $row |
134
|
|
|
* @param Filter $filter |
135
|
|
|
* @return mixed |
136
|
|
|
*/ |
137
|
|
|
protected function applyFilter($row, Filter $filter) |
138
|
|
|
{ |
139
|
|
|
if (is_array($row) || $row instanceof \Traversable) { |
140
|
|
|
if ($filter instanceof FilterDate) { |
141
|
|
|
return $this->applyFilterDate($row, $filter); |
142
|
|
|
} else if ($filter instanceof FilterMultiSelect) { |
143
|
|
|
return $this->applyFilterMultiSelect($row, $filter); |
144
|
|
View Code Duplication |
} else if ($filter instanceof FilterDateRange) { |
|
|
|
|
145
|
|
|
return $this->applyFilterDateRange($row, $filter); |
146
|
|
|
} else if ($filter instanceof FilterRange) { |
147
|
|
|
return $this->applyFilterRange($row, $filter); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
$condition = $filter->getCondition(); |
151
|
|
|
|
152
|
|
|
foreach ($condition as $column => $value) { |
153
|
|
|
$words = explode(' ', $value); |
154
|
|
|
$row_value = strtolower(Strings::toAscii($row[$column])); |
155
|
|
|
|
156
|
|
|
foreach ($words as $word) { |
157
|
|
|
if (FALSE !== strpos($row_value, strtolower(Strings::toAscii($value)))) { |
158
|
|
|
return $row; |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
return FALSE; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Filter by multi select value |
170
|
|
|
* @param mixed $row |
171
|
|
|
* @param FilterMultiSelect $filter |
172
|
|
|
* @return void |
173
|
|
|
*/ |
174
|
|
|
public function applyFilterMultiSelect($row, FilterMultiSelect $filter) |
175
|
|
|
{ |
176
|
|
|
$condition = $filter->getCondition(); |
177
|
|
|
$values = $condition[$filter->getColumn()]; |
178
|
|
|
|
179
|
|
|
return in_array($row[$filter->getColumn()], $values); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @param mixed $row |
185
|
|
|
* @param FilterRange $filter |
186
|
|
|
* @return void |
187
|
|
|
*/ |
188
|
|
|
public function applyFilterRange($row, FilterRange $filter) |
189
|
|
|
{ |
190
|
|
|
$condition = $filter->getCondition(); |
191
|
|
|
$values = $condition[$filter->getColumn()]; |
192
|
|
|
|
193
|
|
View Code Duplication |
if ($values['from'] !== NULL && $values['from'] !== '') { |
|
|
|
|
194
|
|
|
if ($values['from'] > $row[$filter->getColumn()]) { |
195
|
|
|
return FALSE; |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
View Code Duplication |
if ($values['to'] !== NULL && $values['to'] !== '') { |
|
|
|
|
200
|
|
|
if ($values['to'] < $row[$filter->getColumn()]) { |
201
|
|
|
return FALSE; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return TRUE; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* @param mixed $row |
211
|
|
|
* @param FilterDateRange $filter |
212
|
|
|
* @return void |
213
|
|
|
*/ |
214
|
|
|
public function applyFilterDateRange($row, FilterDateRange $filter) |
215
|
|
|
{ |
216
|
|
|
$format = $filter->getPhpFormat(); |
217
|
|
|
$condition = $filter->getCondition(); |
218
|
|
|
$values = $condition[$filter->getColumn()]; |
219
|
|
|
$row_value = $row[$filter->getColumn()]; |
220
|
|
|
|
221
|
|
View Code Duplication |
if ($values['from'] !== NULL && $values['from'] !== '') { |
|
|
|
|
222
|
|
|
$date_from = \DateTime::createFromFormat($format, $values['from']); |
223
|
|
|
|
224
|
|
|
if (!($row_value instanceof \DateTime)) { |
225
|
|
|
/** |
226
|
|
|
* Try to convert string to DateTime object |
227
|
|
|
*/ |
228
|
|
|
try { |
229
|
|
|
$row_value = DateTimeHelper::tryConvertToDate($row_value); |
230
|
|
|
} catch (DataGridDateTimeHelperException $e) { |
231
|
|
|
/** |
232
|
|
|
* Otherwise just return raw string |
233
|
|
|
*/ |
234
|
|
|
return FALSE; |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
if ($row_value->getTimeStamp() < $date_from->getTimeStamp()) { |
239
|
|
|
return FALSE; |
240
|
|
|
} |
241
|
|
|
} |
242
|
|
|
|
243
|
|
View Code Duplication |
if ($values['to'] !== NULL && $values['to'] !== '') { |
|
|
|
|
244
|
|
|
$date_from = \DateTime::createFromFormat($format, $values['to']); |
245
|
|
|
|
246
|
|
|
if (!($row_value instanceof \DateTime)) { |
247
|
|
|
/** |
248
|
|
|
* Try to convert string to DateTime object |
249
|
|
|
*/ |
250
|
|
|
try { |
251
|
|
|
$row_value = DateTimeHelper::tryConvertToDate($row_value); |
252
|
|
|
} catch (DataGridDateTimeHelperException $e) { |
253
|
|
|
/** |
254
|
|
|
* Otherwise just return raw string |
255
|
|
|
*/ |
256
|
|
|
return FALSE; |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
if ($row_value->getTimeStamp() > $date_from->getTimeStamp()) { |
261
|
|
|
return FALSE; |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return TRUE; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Apply fitler date and tell whether row value matches or not |
271
|
|
|
* @param mixed $row |
272
|
|
|
* @param Filter $filter |
273
|
|
|
* @return mixed |
274
|
|
|
*/ |
275
|
|
|
protected function applyFilterDate($row, FilterDate $filter) |
276
|
|
|
{ |
277
|
|
|
$format = $filter->getPhpFormat(); |
278
|
|
|
$condition = $filter->getCondition(); |
279
|
|
|
|
280
|
|
|
foreach ($condition as $column => $value) { |
281
|
|
|
$row_value = $row[$column]; |
282
|
|
|
|
283
|
|
|
$date = \DateTime::createFromFormat($format, $value); |
284
|
|
|
|
285
|
|
|
if (!($row_value instanceof \DateTime)) { |
286
|
|
|
/** |
287
|
|
|
* Try to convert string to DateTime object |
288
|
|
|
*/ |
289
|
|
|
try { |
290
|
|
|
$row_value = DateTimeHelper::tryConvertToDateTime($row_value); |
291
|
|
|
} catch (DataGridDateTimeHelperException $e) { |
292
|
|
|
/** |
293
|
|
|
* Otherwise just return raw string |
294
|
|
|
*/ |
295
|
|
|
return FALSE; |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
return $row_value->format($format) == $date->format($format); |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* Sort data |
306
|
|
|
* @param Sorting $sorting |
307
|
|
|
* @return static |
308
|
|
|
*/ |
309
|
|
|
public function sort(Sorting $sorting) |
310
|
|
|
{ |
311
|
|
|
if (is_callable($sorting->getSortCallback())) { |
312
|
|
|
$this->data = call_user_func( |
|
|
|
|
313
|
|
|
$sorting->getSortCallback(), |
314
|
|
|
$this->data, |
315
|
|
|
$sorting->getSort() |
316
|
|
|
); |
317
|
|
|
|
318
|
|
|
if (!is_array($this->data)) { |
319
|
|
|
throw new DataGridArrayDataSourceException('Sorting callback has to return array'); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
return $this; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
$sort = $sorting->getSort(); |
326
|
|
|
|
327
|
|
|
foreach ($sort as $column => $order) { |
328
|
|
|
$data = []; |
329
|
|
|
|
330
|
|
|
foreach ($this->data as $item) { |
331
|
|
|
$sort_by = (string) $item[$column]; |
332
|
|
|
$data[$sort_by][] = $item; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
if ($order === 'ASC') { |
336
|
|
|
ksort($data); |
337
|
|
|
} else { |
338
|
|
|
krsort($data); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
$this->data = []; |
342
|
|
|
|
343
|
|
|
foreach ($data as $i) { |
344
|
|
|
foreach ($i as $item) { |
345
|
|
|
$this->data[] = $item; |
346
|
|
|
} |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
return $this; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
} |
354
|
|
|
|
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.