Completed
Pull Request — master (#375)
by Dalibor
02:42
created

DibiFluentDataSource::getAggregationData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
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 DibiFluent;
12
use Nette\Utils\Strings;
13
use Ublaboo\DataGrid\Filter;
14
use Ublaboo\DataGrid\Utils\Sorting;
15
use Dibi;
16
17
class DibiFluentDataSource extends FilterableDataSource implements IDataSource
18
{
19
20
	/**
21
	 * @var Dibi\Fluent
22
	 */
23
	protected $data_source;
24
25
	/**
26
	 * @var Dibi\Fluent
27
	 */
28
	protected $aggregation_data_source;
29
30
	/**
31
	 * @var array
32
	 */
33
	protected $data = [];
34
35
	/**
36
	 * @var string
37
	 */
38
	protected $primary_key;
39
40
	/**
41
	 * @var bool
42
	 */
43
	protected $hasAggregationFunctions = FALSE;
44
45
46
	/**
47
	 * @param Dibi\Fluent $data_source
48
	 * @param string $primary_key
49
	 */
50
	public function __construct(Dibi\Fluent $data_source, $primary_key)
51
	{
52
		$this->data_source = $data_source;
53
		$this->aggregation_data_source = $data_source->getConnection();
0 ignored issues
show
Documentation Bug introduced by
It seems like $data_source->getConnection() of type object<Dibi\Connection> is incompatible with the declared type object<Dibi\Fluent> of property $aggregation_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...
54
		$this->primary_key = $primary_key;
55
	}
56
57
58
	/********************************************************************************
59
	 *                          IDataSource implementation                          *
60
	 ********************************************************************************/
61
62
63
	/**
64
	 * Get count of data
65
	 * @return int
66
	 */
67
	public function getCount()
68
	{
69
		return $this->data_source->count();
70
	}
71
72
73
	/**
74
	 * Get the data
75
	 * @return array
76
	 */
77
	public function getData()
78
	{
79
		return $this->data ?: $this->data_source->fetchAll();
80
	}
81
82
83
	/**
84
	 * Filter data - get one row
85
	 * @param array $condition
86
	 * @return static
87
	 */
88
	public function filterOne(array $condition)
89
	{
90
		$this->data_source->where($condition)->limit(1);
91
92
		return $this;
93
	}
94
95
96
	/**
97
	 * Filter by date
98
	 * @param  Filter\FilterDate $filter
99
	 * @return void
100
	 */
101 View Code Duplication
	public function applyFilterDate(Filter\FilterDate $filter)
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...
102
	{
103
		$conditions = $filter->getCondition();
104
105
		$date = \DateTime::createFromFormat($filter->getPhpFormat(), $conditions[$filter->getColumn()]);
106
107
		$this->data_source->where('DATE(%n) = ?', $filter->getColumn(), $date->format('Y-m-d'));
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
108
	}
109
110
111
	/**
112
	 * Filter by date range
113
	 * @param  Filter\FilterDateRange $filter
114
	 * @return void
115
	 */
116 View Code Duplication
	public function applyFilterDateRange(Filter\FilterDateRange $filter)
117
	{
118
		$conditions = $filter->getCondition();
119
120
		$value_from = $conditions[$filter->getColumn()]['from'];
121
		$value_to   = $conditions[$filter->getColumn()]['to'];
122
123
		if ($value_from) {
124
			$date_from = \DateTime::createFromFormat($filter->getPhpFormat(), $value_from);
125
			$date_from->setTime(0, 0, 0);
126
127
			$this->data_source->where('DATE(%n) >= ?', $filter->getColumn(), $date_from);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
128
		}
129
130
		if ($value_to) {
131
			$date_to = \DateTime::createFromFormat($filter->getPhpFormat(), $value_to);
132
			$date_to->setTime(23, 59, 59);
133
134
			$this->data_source->where('DATE(%n) <= ?', $filter->getColumn(), $date_to);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
135
		}
136
	}
137
138
139
	/**
140
	 * Filter by range
141
	 * @param  Filter\FilterRange $filter
142
	 * @return void
143
	 */
144
	public function applyFilterRange(Filter\FilterRange $filter)
145
	{
146
		$conditions = $filter->getCondition();
147
148
		$value_from = $conditions[$filter->getColumn()]['from'];
149
		$value_to   = $conditions[$filter->getColumn()]['to'];
150
151
		if ($value_from || $value_from != '') {
152
			$this->data_source->where('%n >= ?', $filter->getColumn(), $value_from);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
153
		}
154
155
		if ($value_to || $value_to != '') {
156
			$this->data_source->where('%n <= ?', $filter->getColumn(), $value_to);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
157
		}
158
	}
159
160
161
	/**
162
	 * Filter by keyword
163
	 * @param  Filter\FilterText $filter
164
	 * @return void
165
	 */
166
	public function applyFilterText(Filter\FilterText $filter)
167
	{
168
		$condition = $filter->getCondition();
169
		$or = [];
170
171
		foreach ($condition as $column => $value) {
172
			$column = Dibi\Helpers::escape(
173
				$this->data_source->getConnection()->getDriver(),
174
				$column,
175
				\dibi::IDENTIFIER
176
			);
177
178
			if ($filter->isExactSearch()) {
179
				$this->data_source->where("$column = %s", $value);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $value.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
180
				continue;
181
			}
182
183 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...
184
				$words = [$value];
185
			} else {
186
				$words = explode(' ', $value);
187
			}
188
189
			foreach ($words as $word) {
190
				$escaped = $this->data_source->getConnection()->getDriver()->escapeLike($word, 0);
191
192
				if (preg_match("/[\x80-\xFF]/", $escaped)) {
193
					$or[] = "$column LIKE $escaped COLLATE utf8_bin";
194
				} else {
195
					$escaped = Strings::toAscii($escaped);
196
					$or[] = "$column LIKE $escaped COLLATE utf8_general_ci";
197
				}
198
			}
199
		}
200
201 View Code Duplication
		if (sizeof($or) > 1) {
202
			$this->data_source->where('(%or)', $or);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $or.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
203
		} else {
204
			$this->data_source->where($or);
205
		}
206
	}
207
208
209
	/**
210
	 * Filter by multi select value
211
	 * @param  Filter\FilterMultiSelect $filter
212
	 * @return void
213
	 */
214
	public function applyFilterMultiSelect(Filter\FilterMultiSelect $filter)
215
	{
216
		$condition = $filter->getCondition();
217
		$values = $condition[$filter->getColumn()];
218
		$or = [];
0 ignored issues
show
Unused Code introduced by
$or is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
219
220
		if (sizeof($values) > 1) {
221
			$value1 = array_shift($values);
222
			$length = sizeof($values);
223
			$i = 1;
224
225
			$this->data_source->where('(%n = ?', $filter->getColumn(), $value1);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
226
227
			foreach ($values as $value) {
228
				if ($i == $length) {
229
					$this->data_source->or('%n = ?)', $filter->getColumn(), $value);
230
				} else {
231
					$this->data_source->or('%n = ?', $filter->getColumn(), $value);
232
				}
233
234
				$i++;
235
			}
236
		} else {
237
			$this->data_source->where('%n = ?', $filter->getColumn(), reset($values));
0 ignored issues
show
Unused Code introduced by
The call to Fluent::where() has too many arguments starting with $filter->getColumn().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
238
		}
239
	}
240
241
242
	/**
243
	 * Filter by select value
244
	 * @param  Filter\FilterSelect $filter
245
	 * @return void
246
	 */
247
	public function applyFilterSelect(Filter\FilterSelect $filter)
248
	{
249
		$this->data_source->where($filter->getCondition());
250
	}
251
252
253
	/**
254
	 * Apply limit and offset on data
255
	 * @param int $offset
256
	 * @param int $limit
257
	 * @return static
258
	 */
259
	public function limit($offset, $limit)
260
	{
261
		$this->data = $this->data_source->fetchAll($offset, $limit);
262
263
		return $this;
264
	}
265
266
267
	/**
268
	 * Sort data
269
	 * @param  Sorting $sorting
270
	 * @return static
271
	 */
272
	public function sort(Sorting $sorting)
273
	{
274
		if (is_callable($sorting->getSortCallback())) {
275
			call_user_func(
276
				$sorting->getSortCallback(),
277
				$this->data_source,
278
				$sorting->getSort()
279
			);
280
281
			return $this;
282
		}
283
284
		$sort = $sorting->getSort();
285
286
		if (!empty($sort)) {
287
			$this->data_source->removeClause('ORDER BY');
288
			$this->data_source->orderBy($sort);
289
		} else {
290
			/**
291
			 * Has the statement already a order by clause?
292
			 */
293
			$this->data_source->clause('ORDER BY');
294
295
			$reflection = new \ReflectionClass('DibiFluent');
296
			$cursor_property = $reflection->getProperty('cursor');
297
			$cursor_property->setAccessible(TRUE);
298
			$cursor = $cursor_property->getValue($this->data_source);
299
300
			if (!$cursor) {
301
				$this->data_source->orderBy($this->primary_key);
302
			}
303
		}
304
305
		return $this;
306
	}
307
308
	/**
309
	 * @param string $aggregation_type
310
	 * @param string $column
311
	 */
312
	public function addAggregationColumn($aggregation_type, $column)
313
	{
314
		$this->aggregation_data_source = $this->aggregation_data_source->select($aggregation_type .'(%n)', $column)->as($column);
0 ignored issues
show
Unused Code introduced by
The call to Fluent::select() has too many arguments starting with $column.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
315
	}
316
317
318
	/**
319
	 * @return array|Dibi\Row|FALSE
320
	 */
321
	public function getAggregationData()
322
	{
323
		if ($this->hasAggregationFunctions) {
324
			return FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return FALSE; (false) is incompatible with the return type declared by the interface Ublaboo\DataGrid\DataSou...rce::getAggregationData of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
325
		}
326
		$data = $this->aggregation_data_source->from($this->data_source)->as('data')->fetch();
327
		return $data;
328
	}
329
}
330