NextrasDataSource   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 294
Duplicated Lines 1.7 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 32
c 0
b 0
f 0
lcom 1
cbo 10
dl 5
loc 294
rs 9.6

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getCount() 0 4 1
A getData() 0 7 2
A filterOne() 0 10 2
A applyFilterDate() 0 14 2
B applyFilterDateRange() 0 22 4
A applyFilterRange() 0 21 4
B applyFilterText() 5 33 5
A applyFilterMultiSelect() 0 18 2
A applyFilterSelect() 0 4 1
A limit() 0 6 1
B sort() 0 31 5
A prepareColumn() 0 7 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 Nette\Utils\Strings;
12
use Nextras\Orm\Collection\ICollection;
13
use Nextras\Orm\Mapper\Dbal\DbalCollection;
14
use Ublaboo\DataGrid\Filter;
15
use Ublaboo\DataGrid\Utils\ArraysHelper;
16
use Ublaboo\DataGrid\Utils\DateTimeHelper;
17
use Ublaboo\DataGrid\Utils\Sorting;
18
19
class NextrasDataSource extends FilterableDataSource implements IDataSource
20
{
21
22
	/**
23
	 * @var DbalCollection
24
	 */
25
	protected $data_source;
26
27
	/**
28
	 * @var array
29
	 */
30
	protected $data = [];
31
32
	/**
33
	 * @var string
34
	 */
35
	protected $primary_key;
36
37
38
	/**
39
	 * @param ICollection  $data_source
40
	 * @param string       $primary_key
41
	 */
42
	public function __construct(ICollection $data_source, $primary_key)
43
	{
44
		$this->data_source = $data_source;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data_source of type object<Nextras\Orm\Collection\ICollection> is incompatible with the declared type object<Nextras\Orm\Mapper\Dbal\DbalCollection> of property $data_source.

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...
45
		$this->primary_key = $primary_key;
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 $this->data_source->countStored();
61
	}
62
63
64
	/**
65
	 * Get the data
66
	 * @return array
67
	 */
68
	public function getData()
69
	{
70
		/**
71
		 * Paginator is better if the query uses ManyToMany associations
72
		 */
73
		return $this->data ?: $this->data_source->fetchAll();
74
	}
75
76
77
	/**
78
	 * Filter data - get one row
79
	 * @param array $condition
80
	 * @return static
81
	 */
82
	public function filterOne(array $condition)
83
	{
84
		$cond = [];
85
		foreach ($condition as $key => $value) {
86
			$cond[$this->prepareColumn($key)] = $value;
87
		}
88
		$this->data_source = $this->data_source->findBy($cond);
89
90
		return $this;
91
	}
92
93
94
	/**
95
	 * Filter by date
96
	 * @param  Filter\FilterDate $filter
97
	 * @return static
98
	 */
99
	public function applyFilterDate(Filter\FilterDate $filter)
100
	{
101
		foreach ($filter->getCondition() as $column => $value) {
102
			$date = DateTimeHelper::tryConvertToDateTime($value, [$filter->getPhpFormat()]);
103
			$date_end = clone $date;
104
105
			$this->data_source = $this->data_source->findBy([
106
				$this->prepareColumn($column) . '>=' => $date->setTime(0, 0, 0),
107
				$this->prepareColumn($column) . '<=' => $date_end->setTime(23, 59, 59),
108
			]);
109
		}
110
111
		return $this;
112
	}
113
114
115
	/**
116
	 * Filter by date range
117
	 * @param  Filter\FilterDateRange $filter
118
	 * @return void
119
	 */
120
	public function applyFilterDateRange(Filter\FilterDateRange $filter)
121
	{
122
		$conditions = $filter->getCondition();
123
124
		$value_from = $conditions[$filter->getColumn()]['from'];
125
		$value_to = $conditions[$filter->getColumn()]['to'];
126
127
		$dataCondition = [];
128
		if ($value_from) {
129
			$date_from = DateTimeHelper::tryConvertToDateTime($value_from, [$filter->getPhpFormat()]);
130
			$dataCondition[$this->prepareColumn($filter->getColumn()) . '>='] = $date_from->setTime(0, 0, 0);
131
		}
132
133
		if ($value_to) {
134
			$date_to = DateTimeHelper::tryConvertToDateTime($value_to, [$filter->getPhpFormat()]);
135
			$dataCondition[$this->prepareColumn($filter->getColumn()) . '<='] = $date_to->setTime(23, 59, 59);
136
		}
137
138
		if (!empty($dataCondition)) {
139
			$this->data_source = $this->data_source->findBy($dataCondition);
140
		}
141
	}
142
143
144
	/**
145
	 * Filter by range
146
	 * @param  Filter\FilterRange $filter
147
	 * @return void
148
	 */
149
	public function applyFilterRange(Filter\FilterRange $filter)
150
	{
151
		$conditions = $filter->getCondition();
152
153
		$value_from = $conditions[$filter->getColumn()]['from'];
154
		$value_to = $conditions[$filter->getColumn()]['to'];
155
156
		$dataCondition = [];
157
158
		if ($value_from) {
159
			$dataCondition[$this->prepareColumn($filter->getColumn()) . '>='] = $value_from;
160
		}
161
162
		if ($value_to) {
163
			$dataCondition[$this->prepareColumn($filter->getColumn()) . '<='] = $value_to;
164
		}
165
166
		if (!empty($dataCondition)) {
167
			$this->data_source = $this->data_source->findBy($dataCondition);
168
		}
169
	}
170
171
172
	/**
173
	 * Filter by keyword
174
	 * @param  Filter\FilterText $filter
175
	 * @return void
176
	 */
177
	public function applyFilterText(Filter\FilterText $filter)
178
	{
179
		$condition = $filter->getCondition();
180
		$expr = '(';
181
		$params = [];
182
183
		foreach ($condition as $column => $value) {
184
			if ($filter->isExactSearch()) {
185
				$expr .= '%column = %s OR ';
186
				$params[] = $column;
187
				$params[] = "$value";
188
				continue;
189
			}
190
191 View Code Duplication
			if ($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...
192
				$words = [$value];
193
			} else {
194
				$words = explode(' ', $value);
195
			}
196
197
			foreach ($words as $word) {
198
				$expr .= '%column LIKE %s OR ';
199
				$params[] = $column;
200
				$params[] = "%$word%";
201
			}
202
		}
203
204
		$expr = preg_replace('/ OR $/', ')', $expr);
205
206
		array_unshift($params, $expr);
207
208
		call_user_func_array([$this->data_source->getQueryBuilder(), 'andWhere'], $params);
209
	}
210
211
212
	/**
213
	 * Filter by multi select value
214
	 * @param  Filter\FilterMultiSelect $filter
215
	 * @return void
216
	 */
217
	public function applyFilterMultiSelect(Filter\FilterMultiSelect $filter)
218
	{
219
		$condition = $filter->getCondition();
220
		$values = $condition[$filter->getColumn()];
221
		$expr = '(';
222
223
		foreach ($values as $value) {
224
			$expr .= '%column = %any OR ';
225
			$params[] = $filter->getColumn();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
226
			$params[] = "$value";
227
		}
228
229
		$expr = preg_replace('/ OR $/', ')', $expr);
230
231
		array_unshift($params, $expr);
232
233
		call_user_func_array([$this->data_source->getQueryBuilder(), 'andWhere'], $params);
234
	}
235
236
237
	/**
238
	 * Filter by select value
239
	 * @param  Filter\FilterSelect $filter
240
	 * @return void
241
	 */
242
	public function applyFilterSelect(Filter\FilterSelect $filter)
243
	{
244
		$this->data_source = $this->data_source->findBy([$this->prepareColumn($filter->getColumn()) => $filter->getValue()]);
245
	}
246
247
248
	/**
249
	 * Apply limit and offset on data
250
	 * @param int $offset
251
	 * @param int $limit
252
	 * @return static
253
	 */
254
	public function limit($offset, $limit)
255
	{
256
		$this->data_source = $this->data_source->limitBy($limit, $offset);
257
258
		return $this;
259
	}
260
261
262
	/**
263
	 * Sort data
264
	 * @param  Sorting $sorting
265
	 * @return static
266
	 */
267
	public function sort(Sorting $sorting)
268
	{
269
		if (is_callable($sorting->getSortCallback())) {
270
			call_user_func(
271
				$sorting->getSortCallback(),
272
				$this->data_source,
273
				$sorting->getSort()
274
			);
275
276
			return $this;
277
		}
278
279
		$sort = $sorting->getSort();
280
281
		if (!empty($sort)) {
282
			foreach ($sort as $column => $order) {
283
				$this->data_source = $this->data_source->orderBy($this->prepareColumn($column), $order);
284
			}
285
		} else {
286
			/**
287
			 * Has the statement already a order by clause?
288
			 */
289
			$order = $this->data_source->getQueryBuilder()->getClause('order');
290
291
			if (ArraysHelper::testEmpty($order)) {
292
				$this->data_source = $this->data_source->orderBy($this->primary_key);
293
			}
294
		}
295
296
		return $this;
297
	}
298
299
300
		/**
301
		 * Adjust column from DataGrid 'foreignKey.column' to Nextras 'this->foreignKey->column'
302
		 * @param string $column
303
		 * @return string
304
		 */
305
		private function prepareColumn($column)
306
		{
307
			if (Strings::contains($column, '.')) {
308
				return 'this->' . str_replace('.', '->', $column);
309
			}
310
			return $column;
311
		}
312
}
313