Completed
Push — master ( 8de18a...e6e446 )
by Pavel
03:01
created

Column::isTemplateEscaped()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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