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

DoctrineDataSource::limit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2015 ublaboo <[email protected]>
5
 * @author      Jakub Kontra <[email protected]>
6
 * @author      Pavel Janda <[email protected]>
7
 * @package     Ublaboo
8
 */
9
10
namespace Ublaboo\DataGrid\DataSource;
11
12
use Doctrine\ORM\Query;
13
use Doctrine\ORM\QueryBuilder;
14
use Doctrine\ORM\Tools\Pagination\Paginator;
15
use Nette\Utils\Strings;
16
use Ublaboo\DataGrid\Filter;
17
use Ublaboo\DataGrid\Utils\Sorting;
18
19
/**
20
 * @method void onDataLoaded(array $result)
21
 */
22
class DoctrineDataSource extends FilterableDataSource implements IDataSource
23
{
24
25
	/**
26
	 * Event called when datagrid data is loaded.
27
	 * @var callable[]
28
	 */
29
	public $onDataLoaded;
30
31
	/**
32
	 * @var QueryBuilder
33
	 */
34
	protected $data_source;
35
36
	/**
37
	 * @var array
38
	 */
39
	protected $aggregations = [];
40
41
	/**
42
	 * @var string
43
	 */
44
	protected $primary_key;
45
46
	/**
47
	 * @var string
48
	 */
49
	protected $root_alias;
50
51
	/**
52
	 * @var int
53
	 */
54
	protected $placeholder;
55
56
57
	/**
58
	 * @param QueryBuilder $data_source
59
	 * @param string       $primary_key
60
	 */
61
	public function __construct(QueryBuilder $data_source, $primary_key)
62
	{
63
		$this->placeholder = count($data_source->getParameters());
64
		$this->data_source = $data_source;
65
		$this->primary_key = $primary_key;
66
	}
67
68
69
	/**
70
	 * @return \Doctrine\ORM\Query
71
	*/
72
	public function getQuery()
73
	{
74
		return $this->data_source->getQuery();
75
	}
76
77
78
	/**
79
	 * @param  string  $column
80
	 * @return string
81
	 */
82
	private function checkAliases($column)
83
	{
84
		if (Strings::contains($column, ".")) {
85
			return $column;
86
		}
87
88
		if (!isset($this->root_alias)) {
89
			$this->root_alias = $this->data_source->getRootAliases();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->data_source->getRootAliases() of type array is incompatible with the declared type string of property $root_alias.

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...
90
			$this->root_alias = current($this->root_alias);
91
		}
92
93
		return $this->root_alias.'.'.$column;
94
	}
95
96
97
	/********************************************************************************
98
	 *                          IDataSource implementation                          *
99
	 ********************************************************************************/
100
101
102
	/**
103
	 * Get count of data
104
	 * @return int
105
	 */
106
	public function getCount()
107
	{
108
		return (new Paginator($this->getQuery()))->count();
109
	}
110
111
112
	/**
113
	 * Get the data
114
	 * @return array
115
	 */
116
	public function getData()
117
	{
118
		$iterator = (new Paginator($this->getQuery()))->getIterator();
119
120
		$data = iterator_to_array($iterator);
121
122
		$this->onDataLoaded($data);
123
124
		return $data;
125
	}
126
127
128
	/**
129
	 * Filter data - get one row
130
	 * @param  array  $condition
131
	 * @return static
132
	 */
133
	public function filterOne(array $condition)
134
	{
135
		$p = $this->getPlaceholder();
136
137 View Code Duplication
		foreach ($condition as $column => $value) {
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...
138
			$c = $this->checkAliases($column);
139
140
			$this->data_source->andWhere("$c = ?$p")
141
				->setParameter($p, $value);
142
		}
143
144
		return $this;
145
	}
146
147
148
	/**
149
	 * Filter by date
150
	 * @param Filter\FilterDate  $filter
151
	 */
152
	public function applyFilterDate(Filter\FilterDate $filter)
153
	{
154
		$p1 = $this->getPlaceholder();
155
		$p2 = $this->getPlaceholder();
156
157
		foreach ($filter->getCondition() as $column => $value) {
158
			$date = \DateTime::createFromFormat($filter->getPhpFormat(), $value);
159
			$c = $this->checkAliases($column);
160
161
			$this->data_source
162
				->andWhere("$c >= ?$p1")
163
				->andWhere("$c <= ?$p2")
164
				->setParameter($p1, $date->format('Y-m-d 00:00:00'))
165
				->setParameter($p2, $date->format('Y-m-d 23:59:59'));
166
		}
167
	}
168
169
170
	/**
171
	 * Filter by date range
172
	 * @param Filter\FilterDateRange  $filter
173
	 */
174
	public function applyFilterDateRange(Filter\FilterDateRange $filter)
175
	{
176
		$conditions = $filter->getCondition();
177
		$c = $this->checkAliases($filter->getColumn());
0 ignored issues
show
Bug introduced by
It seems like $filter->getColumn() targeting Ublaboo\DataGrid\Filter\Filter::getColumn() can also be of type array; however, Ublaboo\DataGrid\DataSou...aSource::checkAliases() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
178
179
		$value_from = $conditions[$filter->getColumn()]['from'];
180
		$value_to   = $conditions[$filter->getColumn()]['to'];
181
182 View Code Duplication
		if ($value_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...
183
			$date_from = \DateTime::createFromFormat($filter->getPhpFormat(), $value_from);
184
			$date_from->setTime(0, 0, 0);
185
186
			$p = $this->getPlaceholder();
187
188
			$this->data_source->andWhere("$c >= ?$p")->setParameter($p, $date_from->format('Y-m-d H:i:s'));
189
		}
190
191 View Code Duplication
		if ($value_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...
192
			$date_to = \DateTime::createFromFormat($filter->getPhpFormat(), $value_to);
193
			$date_to->setTime(23, 59, 59);
194
195
			$p = $this->getPlaceholder();
196
197
			$this->data_source->andWhere("$c <= ?$p")->setParameter($p, $date_to->format('Y-m-d H:i:s'));
198
		}
199
	}
200
201
202
	/**
203
	 * Filter by range
204
	 * @param Filter\FilterRange  $filter
205
	 */
206
	public function applyFilterRange(Filter\FilterRange $filter)
207
	{
208
		$conditions = $filter->getCondition();
209
		$c = $this->checkAliases($filter->getColumn());
0 ignored issues
show
Bug introduced by
It seems like $filter->getColumn() targeting Ublaboo\DataGrid\Filter\Filter::getColumn() can also be of type array; however, Ublaboo\DataGrid\DataSou...aSource::checkAliases() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
210
211
		$value_from = $conditions[$filter->getColumn()]['from'];
212
		$value_to   = $conditions[$filter->getColumn()]['to'];
213
214
		if ($value_from) {
215
			$p = $this->getPlaceholder();
216
			$this->data_source->andWhere("$c >= ?$p")->setParameter($p, $value_from);
217
		}
218
219
		if ($value_to) {
220
			$p = $this->getPlaceholder();
221
			$this->data_source->andWhere("$c <= ?$p")->setParameter($p, $value_to);
222
		}
223
	}
224
225
226
	/**
227
	 * Filter by keyword
228
	 * @param Filter\FilterText  $filter
229
	 */
230
	public function applyFilterText(Filter\FilterText $filter)
231
	{
232
		$condition = $filter->getCondition();
233
		$exprs = [];
234
235
		foreach ($condition as $column => $value) {
236
			$c = $this->checkAliases($column);
237
238
			if($filter->isExactSearch()){
239
				$exprs[] = $this->data_source->expr()->eq($c, $this->data_source->expr()->literal($value));
240
				continue;
241
			}
242
243 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...
244
				$words = [$value];
245
			} else {
246
				$words = explode(' ', $value);
247
			}
248
249
			foreach ($words as $word) {
250
				$exprs[] = $this->data_source->expr()->like($c, $this->data_source->expr()->literal("%$word%"));
251
			}
252
		}
253
254
		$or = call_user_func_array([$this->data_source->expr(), 'orX'], $exprs);
255
256
		$this->data_source->andWhere($or);
257
	}
258
259
260
	/**
261
	 * Filter by multi select value
262
	 * @param Filter\FilterMultiSelect  $filter
263
	 */
264
	public function applyFilterMultiSelect(Filter\FilterMultiSelect $filter)
265
	{
266
		$c = $this->checkAliases($filter->getColumn());
0 ignored issues
show
Bug introduced by
It seems like $filter->getColumn() targeting Ublaboo\DataGrid\Filter\Filter::getColumn() can also be of type array; however, Ublaboo\DataGrid\DataSou...aSource::checkAliases() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
267
		$p = $this->getPlaceholder();
268
269
		$values = $filter->getCondition()[$filter->getColumn()];
270
		$expr = $this->data_source->expr()->in($c, '?'.$p);
271
272
		$this->data_source->andWhere($expr)->setParameter($p, $values);
273
	}
274
275
276
	/**
277
	 * Filter by select value
278
	 * @param Filter\FilterSelect  $filter
279
	 */
280
	public function applyFilterSelect(Filter\FilterSelect $filter)
281
	{
282
		$p = $this->getPlaceholder();
283
284 View Code Duplication
		foreach ($filter->getCondition() as $column => $value) {
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...
285
			$c = $this->checkAliases($column);
286
287
			$this->data_source->andWhere("$c = ?$p")
288
				->setParameter($p, $value);
289
		}
290
	}
291
292
293
	/**
294
	 * Apply limit and offset on data
295
	 * @param  int  $offset
296
	 * @param  int  $limit
297
	 * @return static
298
	 */
299
	public function limit($offset, $limit)
300
	{
301
		$this->data_source->setFirstResult($offset)->setMaxResults($limit);
302
303
		return $this;
304
	}
305
306
307
	/**
308
	 * Sort data
309
	 * @param  Sorting $sorting
310
	 * @return static
311
	 */
312 View Code Duplication
	public function sort(Sorting $sorting)
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...
313
	{
314
		if (is_callable($sorting->getSortCallback())) {
315
			call_user_func(
316
				$sorting->getSortCallback(),
317
				$this->data_source,
318
				$sorting->getSort()
319
			);
320
321
			return $this;
322
		}
323
324
		$sort = $sorting->getSort();
325
326
		if (!empty($sort)) {
327
			foreach ($sort as $column => $order) {
328
				$this->data_source->addOrderBy($this->checkAliases($column), $order);
329
			}
330
		} else {
331
			/**
332
			 * Has the statement already a order by clause?
333
			 */
334
			if (!$this->data_source->getDQLPart('orderBy')) {
335
				$this->data_source->orderBy($this->checkAliases($this->primary_key));
336
			}
337
		}
338
339
		return $this;
340
	}
341
342
343
	/**
344
	 * Get unique int value for each instance class (self)
345
	 * @return int
346
	 */
347
	public function getPlaceholder()
348
	{
349
		return $this->placeholder++;
350
	}
351
352
	/**
353
	 * @param string $aggregation_type
354
	 * @param string $column
355
	 * @return mixed
356
	 */
357
	public function addAggregationColumn($aggregation_type, $column)
358
	{
359
		$this->aggregations[$column] = $aggregation_type;
360
	}
361
362
	/**
363
	 * get aggregation row
364
	 * @return array
365
	 */
366
	public function getAggregationData()
367
	{
368
		if (count($this->aggregations) === 0) {
369
			return [];
370
		}
371
		$this->data_source->resetDQLPart('select');
372
		foreach ($this->aggregations as $column => $aggregation_type) {
373
			$query = $this->data_source->addSelect("$aggregation_type(" . $this->checkAliases($column) . ") AS $column");
374
		}
375
		return $query->getQuery()->getSingleResult();
0 ignored issues
show
Bug introduced by
The variable $query does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
376
	}
377
}
378