DoctrineDataSource::checkAliases()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
crap 3.0416
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\QueryBuilder;
13
use Doctrine\ORM\Tools\Pagination\Paginator;
14
use Nette\Utils\Strings;
15
use Ublaboo\DataGrid\AggregationFunction\IAggregatable;
16
use Ublaboo\DataGrid\Filter;
17
use Ublaboo\DataGrid\Utils\DateTimeHelper;
18
use Ublaboo\DataGrid\Utils\Sorting;
19
20
/**
21
 * @method void onDataLoaded(array $result)
22
 */
23 1
class DoctrineDataSource extends FilterableDataSource implements IDataSource, IAggregatable
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 string
38
	 */
39
	protected $primary_key;
40
41
	/**
42
	 * @var string
43
	 */
44
	protected $root_alias;
45
46
	/**
47
	 * @var int
48
	 */
49
	protected $placeholder;
50
51
52
	/**
53
	 * @param QueryBuilder $data_source
54
	 * @param string       $primary_key
55
	 */
56
	public function __construct(QueryBuilder $data_source, $primary_key)
57
	{
58 1
		$this->placeholder = count($data_source->getParameters());
59 1
		$this->data_source = $data_source;
60 1
		$this->primary_key = $primary_key;
61 1
	}
62
63
64
	/**
65
	 * @return \Doctrine\ORM\Query
66
	 */
67
	public function getQuery()
68
	{
69 1
		return $this->data_source->getQuery();
70
	}
71
72
73
	/**
74
	 * @param  string  $column
75
	 * @return string
76
	 */
77
	private function checkAliases($column)
78
	{
79 1
		if (Strings::contains($column, '.')) {
80
			return $column;
81
		}
82
83 1
		if (!isset($this->root_alias)) {
84 1
			$this->root_alias = $this->data_source->getRootAliases();
85 1
			$this->root_alias = current($this->root_alias);
86
		}
87
88 1
		return $this->root_alias . '.' . $column;
89
	}
90
91
92
	/**
93
	 * @return bool
94
	 */
95
	private function usePaginator()
96
	{
97 1
		return $this->data_source->getDQLPart('join') || $this->data_source->getDQLPart('groupBy');
98
	}
99
100
101
	/********************************************************************************
102
	 *                          IDataSource implementation                          *
103
	 ********************************************************************************/
104
105
106
	/**
107
	 * Get count of data
108
	 * @return int
109
	 */
110
	public function getCount()
111
	{
112 1
		if ($this->usePaginator()) {
113
			return (new Paginator($this->getQuery()))->count();
114
		}
115 1
		$data_source = clone $this->data_source;
116 1
		$data_source->select(sprintf('COUNT(%s)', $this->checkAliases($this->primary_key)));
117
118 1
		return (int) $data_source->getQuery()->getSingleScalarResult();
119
	}
120
121
122
	/**
123
	 * Get the data
124
	 * @return array
125
	 */
126
	public function getData()
127
	{
128 1
		if ($this->usePaginator()) {
129
			$iterator = (new Paginator($this->getQuery()))->getIterator();
130
131
			$data = iterator_to_array($iterator);
132
		} else {
133 1
			$data = $this->getQuery()->getResult();
134
		}
135
136 1
		$this->onDataLoaded($data);
137
138 1
		return $data;
139
	}
140
141
142
	/**
143
	 * Filter data - get one row
144
	 * @param  array  $condition
145
	 * @return static
146
	 */
147
	public function filterOne(array $condition)
148
	{
149 1
		$p = $this->getPlaceholder();
150
151 1 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...
152 1
			$c = $this->checkAliases($column);
153
154 1
			$this->data_source->andWhere("$c = :$p")
155 1
				->setParameter($p, $value);
156
		}
157
158 1
		return $this;
159
	}
160
161
162
	/**
163
	 * Filter by date
164
	 * @param Filter\FilterDate  $filter
165
	 */
166
	public function applyFilterDate(Filter\FilterDate $filter)
167
	{
168
		$p1 = $this->getPlaceholder();
169
		$p2 = $this->getPlaceholder();
170
171
		foreach ($filter->getCondition() as $column => $value) {
172
			$date = DateTimeHelper::tryConvertToDateTime($value, [$filter->getPhpFormat()]);
173
			$c = $this->checkAliases($column);
174
175
			$this->data_source->andWhere("$c >= :$p1 AND $c <= :$p2")
176
				->setParameter($p1, $date->format('Y-m-d 00:00:00'))
177
				->setParameter($p2, $date->format('Y-m-d 23:59:59'));
178
		}
179
	}
180
181
182
	/**
183
	 * Filter by date range
184
	 * @param Filter\FilterDateRange  $filter
185
	 */
186
	public function applyFilterDateRange(Filter\FilterDateRange $filter)
187
	{
188
		$conditions = $filter->getCondition();
189
		$c = $this->checkAliases($filter->getColumn());
190
191
		$value_from = $conditions[$filter->getColumn()]['from'];
192
		$value_to = $conditions[$filter->getColumn()]['to'];
193
194 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...
195
			$date_from = DateTimeHelper::tryConvertToDate($value_from, [$filter->getPhpFormat()]);
196
			$date_from->setTime(0, 0, 0);
197
198
			$p = $this->getPlaceholder();
199
200
			$this->data_source->andWhere("$c >= :$p")->setParameter($p, $date_from->format('Y-m-d H:i:s'));
201
		}
202
203 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...
204
			$date_to = DateTimeHelper::tryConvertToDate($value_to, [$filter->getPhpFormat()]);
205
			$date_to->setTime(23, 59, 59);
206
207
			$p = $this->getPlaceholder();
208
209
			$this->data_source->andWhere("$c <= :$p")->setParameter($p, $date_to->format('Y-m-d H:i:s'));
210
		}
211
	}
212
213
214
	/**
215
	 * Filter by range
216
	 * @param Filter\FilterRange  $filter
217
	 */
218
	public function applyFilterRange(Filter\FilterRange $filter)
219
	{
220 1
		$conditions = $filter->getCondition();
221 1
		$c = $this->checkAliases($filter->getColumn());
222
223 1
		$value_from = $conditions[$filter->getColumn()]['from'];
224 1
		$value_to = $conditions[$filter->getColumn()]['to'];
225
226 1
		if ($value_from) {
227 1
			$p = $this->getPlaceholder();
228 1
			$this->data_source->andWhere("$c >= :$p")->setParameter($p, $value_from);
229
		}
230
231 1
		if ($value_to) {
232 1
			$p = $this->getPlaceholder();
233 1
			$this->data_source->andWhere("$c <= :$p")->setParameter($p, $value_to);
234
		}
235 1
	}
236
237
238
	/**
239
	 * Filter by keyword
240
	 * @param Filter\FilterText  $filter
241
	 */
242
	public function applyFilterText(Filter\FilterText $filter)
243
	{
244 1
		$condition = $filter->getCondition();
245 1
		$exprs = [];
246
247 1
		foreach ($condition as $column => $value) {
248 1
			$c = $this->checkAliases($column);
249
250 1
			if ($filter->isExactSearch()) {
251 1
				$exprs[] = $this->data_source->expr()->eq($c, $this->data_source->expr()->literal($value));
252 1
				continue;
253
			}
254
255 1 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...
256 1
				$words = [$value];
257
			} else {
258 1
				$words = explode(' ', $value);
259
			}
260
261 1
			foreach ($words as $word) {
262 1
				$exprs[] = $this->data_source->expr()->like($c, $this->data_source->expr()->literal("%$word%"));
263
			}
264
		}
265
266 1
		$or = call_user_func_array([$this->data_source->expr(), 'orX'], $exprs);
267
268 1
		$this->data_source->andWhere($or);
269 1
	}
270
271
272
	/**
273
	 * Filter by multi select value
274
	 * @param Filter\FilterMultiSelect  $filter
275
	 */
276
	public function applyFilterMultiSelect(Filter\FilterMultiSelect $filter)
277
	{
278
		$c = $this->checkAliases($filter->getColumn());
279
		$p = $this->getPlaceholder();
280
281
		$values = $filter->getCondition()[$filter->getColumn()];
282
		$expr = $this->data_source->expr()->in($c, ':' . $p);
283
284
		$this->data_source->andWhere($expr)->setParameter($p, $values);
285
	}
286
287
288
	/**
289
	 * Filter by select value
290
	 * @param Filter\FilterSelect  $filter
291
	 */
292
	public function applyFilterSelect(Filter\FilterSelect $filter)
293
	{
294
		$p = $this->getPlaceholder();
295
296 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...
297
			$c = $this->checkAliases($column);
298
299
			$this->data_source->andWhere("$c = :$p")
300
				->setParameter($p, $value);
301
		}
302
	}
303
304
305
	/**
306
	 * Apply limit and offset on data
307
	 * @param  int  $offset
308
	 * @param  int  $limit
309
	 * @return static
310
	 */
311
	public function limit($offset, $limit)
312
	{
313 1
		$this->data_source->setFirstResult($offset)->setMaxResults($limit);
314
315 1
		return $this;
316
	}
317
318
319
	/**
320
	 * Sort data
321
	 * @param  Sorting $sorting
322
	 * @return static
323
	 */
324 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...
325
	{
326 1
		if (is_callable($sorting->getSortCallback())) {
327
			call_user_func(
328
				$sorting->getSortCallback(),
329
				$this->data_source,
330
				$sorting->getSort()
331
			);
332
333
			return $this;
334
		}
335
336 1
		$sort = $sorting->getSort();
337
338 1
		if (!empty($sort)) {
339 1
			foreach ($sort as $column => $order) {
340 1
				$this->data_source->addOrderBy($this->checkAliases($column), $order);
341
			}
342
		} else {
343
			/**
344
			 * Has the statement already a order by clause?
345
			 */
346
			if (!$this->data_source->getDQLPart('orderBy')) {
347
				$this->data_source->orderBy($this->checkAliases($this->primary_key));
348
			}
349
		}
350
351 1
		return $this;
352
	}
353
354
355
	/**
356
	 * Get unique int value for each instance class (self)
357
	 * @return int
358
	 */
359
	public function getPlaceholder()
360
	{
361 1
		return 'param' . ($this->placeholder++);
362
	}
363
364
365
	/**
366
	 * @param  callable  $aggregationCallback
367
	 * @return void
368
	 */
369
	public function processAggregation(callable $aggregationCallback)
370
	{
371
		call_user_func($aggregationCallback, clone $this->data_source);
372
	}
373
}
374