Completed
Pull Request — master (#375)
by Dalibor
03:13
created

ArrayDataSource::getAggregationData()   C

Complexity

Conditions 14
Paths 33

Size

Total Lines 34
Code Lines 23

Duplication

Lines 16
Ratio 47.06 %

Importance

Changes 0
Metric Value
dl 16
loc 34
rs 5.0864
c 0
b 0
f 0
cc 14
eloc 23
nc 33
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Column\ColumnAggregationFunction;
12
use Ublaboo\DataGrid\Filter\Filter;
13
use Ublaboo\DataGrid\Filter\FilterDate;
14
use Ublaboo\DataGrid\Filter\FilterMultiSelect;
15
use Ublaboo\DataGrid\Filter\FilterRange;
16
use Ublaboo\DataGrid\Filter\FilterDateRange;
17
use Ublaboo\DataGrid\Filter\FilterText;
18
use Nette\Utils\Callback;
19
use Nette\Utils\Strings;
20
use Ublaboo\DataGrid\Exception\DataGridException;
21
use Ublaboo\DataGrid\Exception\DataGridArrayDataSourceException;
22
use Ublaboo\DataGrid\Utils\DateTimeHelper;
23
use Ublaboo\DataGrid\Exception\DataGridDateTimeHelperException;
24
use Ublaboo\DataGrid\Utils\Sorting;
25
26
class ArrayDataSource implements IDataSource
27
{
28
29
	/**
30
	 * @var array
31
	 */
32
	protected $data = [];
33
34
	/**
35
	 * @var array
36
	 */
37
	protected $aggregations = [];
38
39
40
	/**
41
	 * @param array $data_source
42
	 */
43
	public function __construct(array $data_source)
44
	{
45
		$this->data = $data_source;
46
	}
47
48
49
	/********************************************************************************
50
	 *                          IDataSource implementation                          *
51
	 ********************************************************************************/
52
53
54
	/**
55
	 * Get count of data
56
	 * @return int
57
	 */
58
	public function getCount()
59
	{
60
		return sizeof($this->data);
61
	}
62
63
64
	/**
65
	 * Get the data
66
	 * @return array
67
	 * @return array
68
	 */
69
	public function getData()
70
	{
71
		return $this->data;
72
	}
73
74
75
	/**
76
	 * Filter data
77
	 * @param Filter[] $filters
78
	 * @return static
79
	 */
80
	public function filter(array $filters)
81
	{
82
		foreach ($filters as $filter) {
83
			if ($filter->isValueSet()) {
84
				if ($filter->hasConditionCallback()) {
85
					$this->data = (array) call_user_func_array(
86
						$filter->getConditionCallback(),
87
						[$this->data, $filter->getValue()]
88
					);
89
				} else {
90
					$this->data = array_filter($this->data, function($row) use ($filter) {
91
						return $this->applyFilter($row, $filter);
92
					});
93
				}
94
			}
95
		}
96
		
97
		return $this;
98
	}
99
100
101
	/**
102
	 * Filter data - get one row
103
	 * @param array $condition
104
	 * @return ArrayDataSource
105
	 */
106
	public function filterOne(array $condition)
107
	{
108
		foreach ($this->data as $item) {
109
			foreach ($condition as $key => $value) {
110
				if ($item[$key] == $value) {
111
					$this->data = [$item];
112
113
					return $this;
114
				}
115
			}
116
		}
117
118
		$this->data = [];
119
120
		return $this;
121
	}
122
123
124
	/**
125
	 * Apply limit and offset on data
126
	 * @param int $offset
127
	 * @param int $limit
128
	 * @return static
129
	 */
130
	public function limit($offset, $limit)
131
	{
132
		$this->data = array_slice($this->data, $offset, $limit);
133
134
		return $this;
135
	}
136
137
138
	/**
139
	 * Apply fitler and tell whether row passes conditions or not
140
	 * @param  mixed  $row
141
	 * @param  Filter $filter
142
	 * @return mixed
143
	 */
144
	protected function applyFilter($row, Filter $filter)
145
	{
146
		if (is_array($row) || $row instanceof \Traversable) {
147
			if ($filter instanceof FilterDate) {
148
				return $this->applyFilterDate($row, $filter);
149
			} else if ($filter instanceof FilterMultiSelect) {
150
				return $this->applyFilterMultiSelect($row, $filter);
151 View Code Duplication
			} else if ($filter instanceof FilterDateRange) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
152
				return $this->applyFilterDateRange($row, $filter);
153
			} else if ($filter instanceof FilterRange) {
154
				return $this->applyFilterRange($row, $filter);
155
			}
156
157
			$condition = $filter->getCondition();
158
159
			foreach ($condition as $column => $value) {
160
				if ($filter instanceof FilterText && $filter->isExactSearch()) {
161
					return $row[$column] == $value;
162
				}
163
164 View Code Duplication
				if ($filter instanceof FilterText && $filter->hasSplitWordsSearch() === FALSE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
165
					$words = [$value];
166
				} else {
167
					$words = explode(' ', $value);
168
				}
169
170
				$row_value = strtolower(Strings::toAscii($row[$column]));
171
172
				foreach ($words as $word) {
173
					if (FALSE !== strpos($row_value, strtolower(Strings::toAscii($word)))) {
174
						return $row;
175
					}
176
				}
177
			}
178
		}
179
180
		return FALSE;
181
	}
182
183
184
	/**
185
	 * Filter by multi select value
186
	 * @param  mixed  $row
187
	 * @param  FilterMultiSelect $filter
188
	 * @return void
189
	 */
190
	public function applyFilterMultiSelect($row, FilterMultiSelect $filter)
191
	{
192
		$condition = $filter->getCondition();
193
		$values = $condition[$filter->getColumn()];
194
195
		return in_array($row[$filter->getColumn()], $values);
196
	}
197
198
199
	/**
200
	 * @param  mixed  $row
201
	 * @param  FilterRange $filter
202
	 * @return void
203
	 */
204
	public function applyFilterRange($row, FilterRange $filter)
205
	{
206
		$condition = $filter->getCondition();
207
		$values = $condition[$filter->getColumn()];
208
209 View Code Duplication
		if ($values['from'] !== NULL && $values['from'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
210
			if ($values['from'] > $row[$filter->getColumn()]) {
211
				return FALSE;
212
			}
213
		}
214
215 View Code Duplication
		if ($values['to'] !== NULL && $values['to'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
216
			if ($values['to'] < $row[$filter->getColumn()]) {
217
				return FALSE;
218
			}
219
		}
220
221
		return TRUE;
222
	}
223
224
225
	/**
226
	 * @param  mixed  $row
227
	 * @param  FilterDateRange $filter
228
	 * @return void
229
	 */
230
	public function applyFilterDateRange($row, FilterDateRange $filter)
231
	{
232
		$format = $filter->getPhpFormat();
233
		$condition = $filter->getCondition();
234
		$values = $condition[$filter->getColumn()];
235
		$row_value = $row[$filter->getColumn()];
236
237 View Code Duplication
		if ($values['from'] !== NULL && $values['from'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
238
			$date_from = \DateTime::createFromFormat($format, $values['from']);
239
			$date_from->setTime(0, 0, 0);
240
241
			if (!($row_value instanceof \DateTime)) {
242
				/**
243
				 * Try to convert string to DateTime object
244
				 */
245
				try {
246
					$row_value = DateTimeHelper::tryConvertToDate($row_value);
247
				} catch (DataGridDateTimeHelperException $e) {
248
					/**
249
					 * Otherwise just return raw string
250
					 */
251
					return FALSE;
252
				}
253
			}
254
255
			if ($row_value->getTimeStamp() < $date_from->getTimeStamp()) {
256
				return FALSE;
257
			}
258
		}
259
260 View Code Duplication
		if ($values['to'] !== NULL && $values['to'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
261
			$date_to = \DateTime::createFromFormat($format, $values['to']);
262
			$date_to->setTime(23, 59, 59);
263
264
			if (!($row_value instanceof \DateTime)) {
265
				/**
266
				 * Try to convert string to DateTime object
267
				 */
268
				try {
269
					$row_value = DateTimeHelper::tryConvertToDate($row_value);
270
				} catch (DataGridDateTimeHelperException $e) {
271
					/**
272
					 * Otherwise just return raw string
273
					 */
274
					return FALSE;
275
				}
276
			}
277
278
			if ($row_value->getTimeStamp() > $date_to->getTimeStamp()) {
279
				return FALSE;
280
			}
281
		}
282
283
		return TRUE;
284
	}
285
286
287
	/**
288
	 * Apply fitler date and tell whether row value matches or not
289
	 * @param  mixed  $row
290
	 * @param  Filter $filter
291
	 * @return mixed
292
	 */
293
	protected function applyFilterDate($row, FilterDate $filter)
294
	{
295
		$format = $filter->getPhpFormat();
296
		$condition = $filter->getCondition();
297
298
		foreach ($condition as $column => $value) {
299
			$row_value = $row[$column];
300
301
			$date = \DateTime::createFromFormat($format, $value);
302
303
			if (!($row_value instanceof \DateTime)) {
304
				/**
305
				 * Try to convert string to DateTime object
306
				 */
307
				try {
308
					$row_value = DateTimeHelper::tryConvertToDateTime($row_value);
309
				} catch (DataGridDateTimeHelperException $e) {
310
					/**
311
					 * Otherwise just return raw string
312
					 */
313
					return FALSE;
314
				}
315
			}
316
317
			return $row_value->format($format) == $date->format($format);
318
		}
319
	}
320
321
322
	/**
323
	 * Sort data
324
	 * @param  Sorting $sorting
325
	 * @return static
326
	 */
327
	public function sort(Sorting $sorting)
328
	{
329
		if (is_callable($sorting->getSortCallback())) {
330
			$this->data = call_user_func(
0 ignored issues
show
Documentation Bug introduced by
It seems like call_user_func($sorting-...a, $sorting->getSort()) of type * is incompatible with the declared type array of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
331
				$sorting->getSortCallback(),
332
				$this->data,
333
				$sorting->getSort()
334
			);
335
336
			if (!is_array($this->data)) {
337
				throw new DataGridArrayDataSourceException('Sorting callback has to return array');
338
			}
339
340
			return $this;
341
		}
342
343
		$sort = $sorting->getSort();
344
345
		foreach ($sort as $column => $order) {
346
			$data = [];
347
348
			foreach ($this->data as $item) {
349
				if (is_object($item[$column]) && $item[$column] instanceof \DateTime) {
350
					$sort_by = $item[$column]->format('Y-m-d H:i:s');
351
				} else {
352
					$sort_by = (string) $item[$column];
353
				}
354
				$data[$sort_by][] = $item;
355
			}
356
357
			if ($order === 'ASC') {
358
				ksort($data);
359
			} else {
360
				krsort($data);
361
			}
362
363
			$this->data = [];
364
365
			foreach ($data as $i) {
366
				foreach ($i as $item) {
367
					$this->data[] = $item;
368
				}
369
			}
370
		}
371
372
		return $this;
373
	}
374
375
	/**
376
	 * @param string $aggregation_type
377
	 * @param string $column
378
	 * @return mixed
379
	 */
380
	public function addAggregationColumn($aggregation_type, $column)
381
	{
382
		$this->aggregations[$column] = $aggregation_type;
383
	}
384
385
	/**
386
	 * get aggregation row
387
	 * @return array
388
	 */
389
	public function getAggregationData()
390
	{
391
		$result = [];
392
		foreach($this->data as $row){
393
			foreach($this->aggregations as $column => $aggregation_type){
394
				switch($aggregation_type){
395
					case ColumnAggregationFunction::$aggregation_type_sum:
396 View Code Duplication
					case ColumnAggregationFunction::$aggregation_type_avg:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
397
						if(!isset($result[$column])){
398
							$result[$column] = 0;
399
						}
400
						$result[$column] += $row[$column];
401
						break;
402 View Code Duplication
					case ColumnAggregationFunction::$aggregation_type_min:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
403
						if(!isset($result[$column]) || $row[$column] < $result[$column]){
404
							$result[$column] = $row[$column];
405
						}
406
						break;
407 View Code Duplication
					case ColumnAggregationFunction::$aggregation_type_max:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
408
						if(!isset($result[$column]) || $row[$column] > $result[$column]){
409
							$result[$column] = $row[$column];
410
						}
411
						break;
412
				}
413
			}
414
		}
415
416
		foreach($this->aggregations as $column => $aggregation_type) {
417
			if($aggregation_type == ColumnAggregationFunction::$aggregation_type_avg){
418
				$result[$column] =  $result[$column] / count($this->data);
419
			}
420
		}
421
		return $result;
422
	}
423
}
424