Completed
Pull Request — master (#315)
by Martin
03:14
created

DoctrineDataSource::setPostFetchCallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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