Completed
Pull Request — master (#375)
by Dalibor
05:23
created

ArrayDataSource::getAggregationData()   C

Complexity

Conditions 14
Paths 33

Size

Total Lines 34
Code Lines 23

Duplication

Lines 34
Ratio 100 %

Importance

Changes 0
Metric Value
dl 34
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
	 * @var int
41
	 */
42
	protected $count = 0;
43
44
45
	/**
46
	 * @param array $data_source
47
	 */
48
	public function __construct(array $data_source)
49
	{
50
		$this->data = $data_source;
51
		$this->count = sizeof($data_source);
52
	}
53
54
55
	/********************************************************************************
56
	 *                          IDataSource implementation                          *
57
	 ********************************************************************************/
58
59
60
	/**
61
	 * Get count of data
62
	 * @return int
63
	 */
64
	public function getCount()
65
	{
66
		return $this->count;
67
	}
68
69
70
	/**
71
	 * Get the data
72
	 * @return array
73
	 * @return array
74
	 */
75
	public function getData()
76
	{
77
		return $this->data;
78
	}
79
80
81
	/**
82
	 * Filter data
83
	 * @param Filter[] $filters
84
	 * @return static
85
	 */
86
	public function filter(array $filters)
87
	{
88
		foreach ($filters as $filter) {
89
			if ($filter->isValueSet()) {
90
				if ($filter->hasConditionCallback()) {
91
					$this->data = (array) call_user_func_array(
92
						$filter->getConditionCallback(),
93
						[$this->data, $filter->getValue()]
94
					);
95
				} else {
96
					$this->data = array_filter($this->data, function ($row) use ($filter) {
97
						return $this->applyFilter($row, $filter);
98
					});
99
				}
100
			}
101
		}
102
		
103
		return $this;
104
	}
105
106
107
	/**
108
	 * Filter data - get one row
109
	 * @param array $condition
110
	 * @return ArrayDataSource
111
	 */
112
	public function filterOne(array $condition)
113
	{
114
		foreach ($this->data as $item) {
115
			foreach ($condition as $key => $value) {
116
				if ($item[$key] == $value) {
117
					$this->data = [$item];
118
119
					return $this;
120
				}
121
			}
122
		}
123
124
		$this->data = [];
125
126
		return $this;
127
	}
128
129
130
	/**
131
	 * Apply limit and offset on data
132
	 * @param int $offset
133
	 * @param int $limit
134
	 * @return static
135
	 */
136
	public function limit($offset, $limit)
137
	{
138
		$this->data = array_slice($this->data, $offset, $limit);
139
140
		return $this;
141
	}
142
143
144
	/**
145
	 * Apply fitler and tell whether row passes conditions or not
146
	 * @param  mixed  $row
147
	 * @param  Filter $filter
148
	 * @return mixed
149
	 */
150
	protected function applyFilter($row, Filter $filter)
151
	{
152
		if (is_array($row) || $row instanceof \Traversable) {
153
			if ($filter instanceof FilterDate) {
154
				return $this->applyFilterDate($row, $filter);
155
			} else if ($filter instanceof FilterMultiSelect) {
156
				return $this->applyFilterMultiSelect($row, $filter);
157
			} else if ($filter instanceof FilterDateRange) {
158
				return $this->applyFilterDateRange($row, $filter);
159
			} else if ($filter instanceof FilterRange) {
160
				return $this->applyFilterRange($row, $filter);
161
			}
162
163
			$condition = $filter->getCondition();
164
165
			foreach ($condition as $column => $value) {
166
				if ($filter instanceof FilterText && $filter->isExactSearch()) {
167
					return $row[$column] == $value;
168
				}
169
170 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...
171
					$words = [$value];
172
				} else {
173
					$words = explode(' ', $value);
174
				}
175
176
				$row_value = strtolower(Strings::toAscii($row[$column]));
177
178
				foreach ($words as $word) {
179
					if (FALSE !== strpos($row_value, strtolower(Strings::toAscii($word)))) {
180
						return $row;
181
					}
182
				}
183
			}
184
		}
185
186
		return FALSE;
187
	}
188
189
190
	/**
191
	 * Filter by multi select value
192
	 * @param  mixed  $row
193
	 * @param  FilterMultiSelect $filter
194
	 * @return void
195
	 */
196
	public function applyFilterMultiSelect($row, FilterMultiSelect $filter)
197
	{
198
		$condition = $filter->getCondition();
199
		$values = $condition[$filter->getColumn()];
200
201
		return in_array($row[$filter->getColumn()], $values);
202
	}
203
204
205
	/**
206
	 * @param  mixed  $row
207
	 * @param  FilterRange $filter
208
	 * @return void
209
	 */
210
	public function applyFilterRange($row, FilterRange $filter)
211
	{
212
		$condition = $filter->getCondition();
213
		$values = $condition[$filter->getColumn()];
214
215 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...
216
			if ($values['from'] > $row[$filter->getColumn()]) {
217
				return FALSE;
218
			}
219
		}
220
221 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...
222
			if ($values['to'] < $row[$filter->getColumn()]) {
223
				return FALSE;
224
			}
225
		}
226
227
		return TRUE;
228
	}
229
230
231
	/**
232
	 * @param  mixed  $row
233
	 * @param  FilterDateRange $filter
234
	 * @return void
235
	 */
236
	public function applyFilterDateRange($row, FilterDateRange $filter)
237
	{
238
		$format = $filter->getPhpFormat();
239
		$condition = $filter->getCondition();
240
		$values = $condition[$filter->getColumn()];
241
		$row_value = $row[$filter->getColumn()];
242
243 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...
244
			$date_from = \DateTime::createFromFormat($format, $values['from']);
245
			$date_from->setTime(0, 0, 0);
246
247
			if (!($row_value instanceof \DateTime)) {
248
				/**
249
				 * Try to convert string to DateTime object
250
				 */
251
				try {
252
					$row_value = DateTimeHelper::tryConvertToDate($row_value);
253
				} catch (DataGridDateTimeHelperException $e) {
254
					/**
255
					 * Otherwise just return raw string
256
					 */
257
					return FALSE;
258
				}
259
			}
260
261
			if ($row_value->getTimeStamp() < $date_from->getTimeStamp()) {
262
				return FALSE;
263
			}
264
		}
265
266 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...
267
			$date_to = \DateTime::createFromFormat($format, $values['to']);
268
			$date_to->setTime(23, 59, 59);
269
270
			if (!($row_value instanceof \DateTime)) {
271
				/**
272
				 * Try to convert string to DateTime object
273
				 */
274
				try {
275
					$row_value = DateTimeHelper::tryConvertToDate($row_value);
276
				} catch (DataGridDateTimeHelperException $e) {
277
					/**
278
					 * Otherwise just return raw string
279
					 */
280
					return FALSE;
281
				}
282
			}
283
284
			if ($row_value->getTimeStamp() > $date_to->getTimeStamp()) {
285
				return FALSE;
286
			}
287
		}
288
289
		return TRUE;
290
	}
291
292
293
	/**
294
	 * Apply fitler date and tell whether row value matches or not
295
	 * @param  mixed  $row
296
	 * @param  Filter $filter
297
	 * @return mixed
298
	 */
299
	protected function applyFilterDate($row, FilterDate $filter)
300
	{
301
		$format = $filter->getPhpFormat();
302
		$condition = $filter->getCondition();
303
304
		foreach ($condition as $column => $value) {
305
			$row_value = $row[$column];
306
307
			$date = \DateTime::createFromFormat($format, $value);
308
309
			if (!($row_value instanceof \DateTime)) {
310
				/**
311
				 * Try to convert string to DateTime object
312
				 */
313
				try {
314
					$row_value = DateTimeHelper::tryConvertToDateTime($row_value);
315
				} catch (DataGridDateTimeHelperException $e) {
316
					/**
317
					 * Otherwise just return raw string
318
					 */
319
					return FALSE;
320
				}
321
			}
322
323
			return $row_value->format($format) == $date->format($format);
324
		}
325
	}
326
327
328
	/**
329
	 * Sort data
330
	 * @param  Sorting $sorting
331
	 * @return static
332
	 */
333
	public function sort(Sorting $sorting)
334
	{
335
		if (is_callable($sorting->getSortCallback())) {
336
			$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...
337
				$sorting->getSortCallback(),
338
				$this->data,
339
				$sorting->getSort()
340
			);
341
342
			if (!is_array($this->data)) {
343
				throw new DataGridArrayDataSourceException('Sorting callback has to return array');
344
			}
345
346
			return $this;
347
		}
348
349
		$sort = $sorting->getSort();
350
351
		foreach ($sort as $column => $order) {
352
			$data = [];
353
354
			foreach ($this->data as $item) {
355
				if (is_object($item[$column]) && $item[$column] instanceof \DateTime) {
356
					$sort_by = $item[$column]->format('Y-m-d H:i:s');
357
				} else {
358
					$sort_by = (string) $item[$column];
359
				}
360
				$data[$sort_by][] = $item;
361
			}
362
363
			if ($order === 'ASC') {
364
				ksort($data);
365
			} else {
366
				krsort($data);
367
			}
368
369
			$this->data = [];
370
371
			foreach ($data as $i) {
372
				foreach ($i as $item) {
373
					$this->data[] = $item;
374
				}
375
			}
376
		}
377
378
		return $this;
379
	}
380
381
	/**
382
	 * @param string $aggregation_type
383
	 * @param string $column
384
	 * @return mixed
385
	 */
386
	public function addAggregationColumn($aggregation_type, $column)
387
	{
388
		$this->aggregations[$column] = $aggregation_type;
389
	}
390
391
	/**
392
	 * get aggregation row
393
	 * @return array
394
	 */
395 View Code Duplication
	public function getAggregationData()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
396
	{
397
		$result = [];
398
		foreach ($this->data as $row) {
399
			foreach ($this->aggregations as $column => $aggregation_type) {
400
				switch ($aggregation_type) {
401
					case ColumnAggregationFunction::$aggregation_type_sum:
402
					case ColumnAggregationFunction::$aggregation_type_avg:
403
						if (!isset($result[$column])) {
404
							$result[$column] = 0;
405
						}
406
						$result[$column] += $row[$column];
407
						break;
408
					case ColumnAggregationFunction::$aggregation_type_min:
409
						if (!isset($result[$column]) || $row[$column] < $result[$column]) {
410
							$result[$column] = $row[$column];
411
						}
412
						break;
413
					case ColumnAggregationFunction::$aggregation_type_max:
414
						if (!isset($result[$column]) || $row[$column] > $result[$column]) {
415
							$result[$column] = $row[$column];
416
						}
417
						break;
418
				}
419
			}
420
		}
421
422
		foreach ($this->aggregations as $column => $aggregation_type) {
423
			if ($aggregation_type == ColumnAggregationFunction::$aggregation_type_avg) {
424
				$result[$column] =  $result[$column] / count($this->data);
425
			}
426
		}
427
		return $result;
428
	}
429
}
430