Completed
Pull Request — master (#375)
by Dalibor
05:23
created

Column::getAggregationFunction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
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\Column;
10
11
use Ublaboo;
12
use Ublaboo\DataGrid\DataGrid;
13
use Ublaboo\DataGrid\Row;
14
use Ublaboo\DataGrid\Exception\DataGridException;
15
use Ublaboo\DataGrid\Exception\DataGridColumnRendererException;
16
use Nette\Utils\Html;
17
use Ublaboo\DataGrid\Traits;
18
19
abstract class Column extends FilterableColumn
20
{
21
22
	use Traits\TLink;
23
24
	/**
25
	 * @var array
26
	 */
27
	protected $replacements = [];
28
29
	/**
30
	 * @var Renderer|NULL
31
	 */
32
	protected $renderer;
33
34
	/**
35
	 * @var string
36
	 */
37
	protected $template;
38
39
	/**
40
	 * @var bool|string
41
	 */
42
	protected $sortable = FALSE;
43
44
	/**
45
	 * @var bool
46
	 */
47
	protected $sortable_reset_pagination = FALSE;
48
49
	/**
50
	 * @var null|callable
51
	 */
52
	protected $sortable_callback = NULL;
53
54
	/**
55
	 * @var array
56
	 */
57
	protected $sort;
58
59
	/**
60
	 * @var bool
61
	 */
62
	protected $template_escaping = TRUE;
63
64
	/**
65
	 * @var bool
66
	 */
67
	protected $header_escaping = FALSE;
68
69
	
70
	/**
71
	 * @var string
72
	 */
73
	protected $align;
74
75
76
	/**
77
	 * @var array
78
	 */
79
	protected $template_variables = [];
80
81
	/**
82
	 * @var callable
83
	 */
84
	protected $editable_callback;
85
86
	/**
87
	 * @var array
88
	 */
89
	protected $editable_element = ['textarea', ['class' => 'form-control']];
90
91
	/**
92
	 * Cached html elements
93
	 * @var array
94
	 */
95
	protected $el_cache = [];
96
97
	/**
98
	 * @var bool
99
	 */
100
	protected $default_hide = FALSE;
101
102
	/**
103
	 * @var ColumnAggregationFunction
104
	 */
105
	protected $aggregation_function = NULL;
106
107
108
	/**
109
	 * Render row item into template
110
	 * @param  Row   $row
111
	 * @return mixed
112
	 */
113
	public function render(Row $row)
114
	{
115
		/**
116
		 * Renderer function may be used
117
		 */
118
		try {
119
			return $this->useRenderer($row);
120
		} catch (DataGridColumnRendererException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
121
			/**
122
			 * Do not use renderer
123
			 */
124
		}
125
126
		/**
127
		 * Or replacements may be applied
128
		 */
129
		list($do_replace, $replaced) = $this->applyReplacements($row);
130
		if ($do_replace) {
131
			return $replaced;
132
		}
133
134
		return $this->getColumnValue($row);
135
	}
136
137
138
	/**
139
	 * Try to render item with custom renderer
140
	 * @param  Row   $row
141
	 * @return mixed
142
	 */
143
	public function useRenderer(Row $row)
144
	{
145
		$renderer = $this->getRenderer();
146
147
		if (!$renderer) {
148
			throw new DataGridColumnRendererException;
149
		}
150
151
		if ($renderer->getConditionCallback()) {
152
			if (!call_user_func_array($renderer->getConditionCallback(), [$row->getItem()])) {
153
				throw new DataGridColumnRendererException;
154
			}
155
156
			return call_user_func_array($renderer->getCallback(), [$row->getItem()]);
157
		}
158
159
		return call_user_func_array($renderer->getCallback(), [$row->getItem()]);
160
	}
161
162
163
	/**
164
	 * Should be column values escaped in latte?
165
	 * @param bool $template_escaping
166
	 */
167
	public function setTemplateEscaping($template_escaping = TRUE)
168
	{
169
		$this->template_escaping = (bool) $template_escaping;
170
171
		return $this;
172
	}
173
174
175
	public function isTemplateEscaped()
176
	{
177
		return $this->template_escaping;
178
	}
179
180
	/**
181
	 * Should be column header escaped in latte?
182
	 * @param bool $header_escaping
183
	 */
184
	public function setHeaderEscaping($header_escaping = FALSE)
185
	{
186
		$this->header_escaping = (bool) $header_escaping;
187
188
		return $this;
189
	}
190
	
191
	public function isHeaderEscaped()
192
	{
193
		return $this->header_escaping;
194
	}
195
	
196
	
197
	/**
198
	 * Set column sortable or not
199
	 * @param bool|string $sortable
200
	 */
201
	public function setSortable($sortable = TRUE)
202
	{
203
		$this->sortable = is_string($sortable) ? $sortable : (bool) $sortable;
204
205
		return $this;
206
	}
207
208
209
	/**
210
	 * Tell whether column is sortable
211
	 * @return bool
212
	 */
213
	public function isSortable()
214
	{
215
		return (bool) $this->sortable;
216
	}
217
218
219
	/**
220
	 * Shoud be the pagination reseted after sorting?
221
	 * @param bool $sortable_reset_pagination
222
	 * @return static
223
	 */
224
	public function setSortableResetPagination($sortable_reset_pagination = TRUE)
225
	{
226
		$this->sortable_reset_pagination = (bool) $sortable_reset_pagination;
227
228
		return $this;
229
	}
230
231
232
	/**
233
	 * Do reset pagination after sorting?
234
	 * @return bool
235
	 */
236
	public function sortableResetPagination()
237
	{
238
		return $this->sortable_reset_pagination;
239
	}
240
241
242
	/**
243
	 * Set custom ORDER BY clause
244
	 * @param callable $sortable_callback
245
	 * @return static
246
	 */
247
	public function setSortableCallback(callable $sortable_callback)
248
	{
249
		$this->sortable_callback = $sortable_callback;
250
251
		return $this;
252
	}
253
254
255
	/**
256
	 * Get custom ORDER BY clause
257
	 * @return callable|null
258
	 */
259
	public function getSortableCallback()
260
	{
261
		return $this->sortable_callback;
262
	}
263
264
265
	/**
266
	 * Get column to sort by
267
	 * @return string
268
	 */
269
	public function getSortingColumn()
270
	{
271
		return is_string($this->sortable) ? $this->sortable : $this->column;
272
	}
273
274
275
	/**
276
	 * Get column name
277
	 * @return string
278
	 */
279
	public function getColumnName()
280
	{
281
		return $this->column;
282
	}
283
284
285
	/**
286
	 * Get column value of row item
287
	 * @param  Row   $row
288
	 * @return mixed
289
	 */
290
	public function getColumnValue(Row $row)
291
	{
292
		return $row->getValue($this->column);
293
	}
294
295
296
	/**
297
	 * @return string
298
	 */
299
	public function getName()
300
	{
301
		return $this->name;
302
	}
303
304
305
	/**
306
	 * Set column replacements
307
	 * @param  array $replacements
308
	 * @return Column
309
	 */
310
	public function setReplacement(array $replacements)
311
	{
312
		$this->replacements = $replacements;
313
314
		return $this;
315
	}
316
317
318
	/**
319
	 * Tell whether columns has replacements
320
	 * @return bool
321
	 */
322
	public function hasReplacements()
323
	{
324
		return (bool) $this->replacements;
325
	}
326
327
328
	/**
329
	 * Apply replacements
330
	 * @param  Row   $row
331
	 * @return array
332
	 */
333
	public function applyReplacements(Row $row)
334
	{
335
		$value = $row->getValue($this->column);
336
337
		if ((is_scalar($value) || is_null($value)) && isset($this->replacements[$value])) {
338
			return [TRUE, $this->replacements[$value]];
339
		}
340
341
		return [FALSE, NULL];
342
	}
343
344
345
	/**
346
	 * Set renderer callback and (it may be optional - the condition callback will decide)
347
	 * @param callable $renderer
348
	 */
349
	public function setRenderer($renderer, $condition_callback = NULL)
350
	{
351
		if ($this->hasReplacements()) {
352
			throw new DataGridException(
353
				"Use either Column::setReplacement() or Column::setRenderer, not both."
354
			);
355
		}
356
357
		if (!is_callable($renderer)) {
358
			throw new DataGridException(
359
				"Renderer (method Column::setRenderer()) must be callable."
360
			);
361
		}
362
363
		if (NULL != $condition_callback && !is_callable($condition_callback)) {
364
			throw new DataGridException(
365
				"Renderer (method Column::setRenderer()) must be callable."
366
			);
367
		}
368
369
		$this->renderer = new Renderer($renderer, $condition_callback);
370
371
		return $this;
372
	}
373
374
375
	/**
376
	 * Set renderer callback just if condition is truthy
377
	 * @param callable $renderer
378
	 */
379
	public function setRendererOnCondition($renderer, $condition_callback)
380
	{
381
		return $this->setRenderer($renderer, $condition_callback);
382
	}
383
384
385
	/**
386
	 * Return custom renderer callback
387
	 * @return Renderer|null
388
	 */
389
	public function getRenderer()
390
	{
391
		return $this->renderer;
392
	}
393
394
395
	/**
396
	 * Column may have its own template
397
	 * @param string $template
398
	 */
399
	public function setTemplate($template, array $template_variables = [])
400
	{
401
		$this->template = $template;
402
		$this->template_variables = $template_variables;
403
404
		return $this;
405
	}
406
407
408
	/**
409
	 * Column can have variables that will be passed to custom template scope
410
	 * @return array
411
	 */
412
	public function getTemplateVariables()
413
	{
414
		return $this->template_variables;
415
	}
416
417
418
	/**
419
	 * Tell whether column has its owntemplate
420
	 * @return bool
421
	 */
422
	public function hasTemplate()
423
	{
424
		return (bool) $this->template;
425
	}
426
427
428
	/**
429
	 * Get column template path
430
	 * @return string
431
	 */
432
	public function getTemplate()
433
	{
434
		return $this->template;
435
	}
436
437
438
	/**
439
	 * Tell whether data source is sorted by this collumn
440
	 * @return bool
441
	 */
442
	public function isSortedBy()
443
	{
444
		return (bool) $this->sort;
445
	}
446
447
448
	/**
449
	 * Tell column his sorting options
450
	 * @param array $sort
451
	 */
452
	public function setSort(array $sort)
453
	{
454
		$this->sort = $sort[$this->key];
455
456
		return $this;
457
	}
458
459
460
	/**
461
	 * What sorting will be applied after next click?
462
	 * @return array
463
	 */
464
	public function getSortNext()
465
	{
466
		if ($this->sort == 'ASC') {
467
			return [$this->key => 'DESC'];
468
		} else if ($this->sort == 'DESC') {
469
			return [$this->key => FALSE];
470
		}
471
472
		return [$this->key => 'ASC'];
473
	}
474
475
476
	/**
477
	 * @return bool
478
	 */
479
	public function hasSortNext()
480
	{
481
		foreach ($this->getSortNext() as $key => $order) {
482
			return $order !== FALSE;
483
		}
484
	}
485
486
487
	/**
488
	 * Is sorting ascending?
489
	 * @return bool
490
	 */
491
	public function isSortAsc()
492
	{
493
		return $this->sort == 'ASC';
494
	}
495
496
497
	/**
498
	 * Set column alignment
499
	 * @param string $align
500
	 */
501
	public function setAlign($align)
502
	{
503
		$this->align = (string) $align;
504
505
		return $this;
506
	}
507
508
509
	/**
510
	 * Has column some alignment?
511
	 * @return bool [description]
512
	 */
513
	public function hasAlign()
514
	{
515
		return (bool) $this->align;
516
	}
517
518
519
	/**
520
	 * Get column alignment
521
	 * @return string
522
	 */
523
	public function getAlign()
524
	{
525
		return $this->align ?: 'left';
526
	}
527
528
529
	/**
530
	 * Set column content fit
531
	 * @param bool $fit_content
532
	 * @return $this
533
	 */
534
	public function setFitContent($fit_content = TRUE)
535
	{
536
		($fit_content) ? $this->addAttributes(['class' => 'datagrid-fit-content']) : NULL;
537
538
		return $this;
539
	}
540
541
542
	/**
543
	 * Set callback that will be called after inline editing
544
	 * @param callable $editable_callback
545
	 */
546
	public function setEditableCallback(callable $editable_callback)
547
	{
548
		$this->editable_callback = $editable_callback;
549
550
		return $this;
551
	}
552
553
554
	/**
555
	 * Return callback that is used after inline editing
556
	 * @return callable
557
	 */
558
	public function getEditableCallback()
559
	{
560
		return $this->editable_callback;
561
	}
562
563
564
	/**
565
	 * Is column editable?
566
	 * @return bool
567
	 */
568
	public function isEditable()
569
	{
570
		return (bool) $this->getEditableCallback();
571
	}
572
573
574
	/**
575
	 * Element is by default textarea, user can change that
576
	 * @param string $el_type
577
	 * @param array  $attrs
578
	 * @return static
579
	 */
580
	public function setEditableInputType($el_type, array $attrs = [])
581
	{
582
		$this->editable_element = [$el_type, $attrs];
583
584
		return $this;
585
	}
586
587
588
	/**
589
	 * Change small inline edit input type to select
590
	 * @param array  $options
591
	 * @return static
592
	 */
593
	public function setEditableInputTypeSelect(array $options = [])
594
	{
595
		$select = Html::el('select');
596
597
		foreach ($options as $value => $text) {
598
			$select->create('option')
599
				->value($value)
600
				->setText($text);
601
		}
602
603
		$this->addAttributes(['data-datagrid-editable-element' => (string) $select]);
604
605
		return $this->setEditableInputType('select');
606
	}
607
608
609
	/**
610
	 * [getEditableInputType description]
611
	 * @return array
612
	 */
613
	public function getEditableInputType()
614
	{
615
		return $this->editable_element;
616
	}
617
618
619
	/**
620
	 * Set attributes for both th and td element
621
	 * @param array $attrs
622
	 * @return static
623
	 */
624
	public function addAttributes(array $attrs)
625
	{
626
		$this->getElementPrototype('td')->addAttributes($attrs);
627
		$this->getElementPrototype('th')->addAttributes($attrs);
628
629
		return $this;
630
	}
631
632
633
	/**
634
	 * Get th/td column element
635
	 * @param  string   $tag th|td
636
	 * @param  string   $key
637
	 * @param  Row|NULL $row
638
	 * @return Html
639
	 */
640
	public function getElementPrototype($tag, $key = NULL, Row $row = NULL)
641
	{
642
		/**
643
		 * Get cached element
644
		 */
645
		if (empty($this->el_cache[$tag])) {
646
			$this->el_cache[$tag] = $el = $el = Html::el($tag);
647
		} else {
648
			$el = $this->el_cache[$tag];
649
		}
650
651
		/**
652
		 * If class was set by user via $el->class = '', fix it
653
		 */
654
		if (!empty($el->class) && is_string($el->class)) {
655
			$class = $el->class;
656
			unset($el->class);
657
658
			$el->class[] = $class;
659
		}
660
661
		$el->class[] = "text-{$this->getAlign()}";
662
663
		/**
664
		 * Method called from datagrid template, set appropriate classes and another attributes
665
		 */
666
		if ($key !== NULL && $row !== NULL) {
667
			$el->class[] = "col-{$key}";
668
669
			if ($tag == 'td') {
670
				if ($this->isEditable()) {
671
					$link = $this->grid->link('edit!', ['key' => $key, 'id' => $row->getId()]);
672
673
					$el->data('datagrid-editable-url', $link);
674
675
					$el->data('datagrid-editable-type', $this->editable_element[0]);
676
					$el->data('datagrid-editable-attrs', json_encode($this->editable_element[1]));
677
				}
678
			}
679
		}
680
681
		return $el;
682
	}
683
684
685
	/**
686
	 * @param bool $default_hide
687
	 * @return static
688
	 */
689
	public function setDefaultHide($default_hide = TRUE)
690
	{
691
		$this->default_hide = (bool) $default_hide;
692
693
		if ($default_hide) {
694
			$this->grid->setSomeColumnDefaultHide($default_hide);
695
		}
696
697
		return $this;
698
	}
699
700
701
	public function getDefaultHide()
702
	{
703
		return $this->default_hide;
704
	}
705
706
707
	/**
708
	 * Get row item params (E.g. action may be called id => $item->id, name => $item->name, ...)
709
	 * @param  Row   $row
710
	 * @param  array $params_list
711
	 * @return array
712
	 */
713
	protected function getItemParams(Row $row, array $params_list)
714
	{
715
		$return = [];
716
717
		foreach ($params_list as $param_name => $param) {
718
			$return[is_string($param_name) ? $param_name : $param] = $row->getValue($param);
719
		}
720
721
		return $return;
722
	}
723
724
725
	/**
726
	 * @return string
727
	 */
728
	public function getColumn()
729
	{
730
		return $this->column;
731
	}
732
733
734
	/**
735
	 * @return ColumnAggregationFunction
736
	 */
737
	public function getAggregationFunction()
738
	{
739
		return $this->aggregation_function;
740
	}
741
742
743
	/**
744
	 * @return bool
745
	 */
746
	public function hasAggregationFunction()
747
	{
748
		return $this->aggregation_function instanceof ColumnAggregationFunction;
749
	}
750
751
752
	/**
753
	 * @param $aggregation_type use ColumnAggregationFunction::$aggregation_type...
754
	 * @param string $column
755
	 * @return ColumnAggregationFunction
756
	 */
757
	public function setAggregationFunction($aggregation_type, $column = NULL)
758
	{
759
		if ($column === NULL) {
760
			$column = $this->column;
761
		}
762
		$this->aggregation_function = new ColumnAggregationFunction($aggregation_type, $column);
763
		return $this->aggregation_function;
764
	}
765
}
766