Completed
Pull Request — master (#390)
by
unknown
07:31
created

Column::setTranslatable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
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 $translatable = TRUE;
48
	
49
	/**
50
	 * @var bool
51
	 */
52
	protected $sortable_reset_pagination = FALSE;
53
54
	/**
55
	 * @var null|callable
56
	 */
57
	protected $sortable_callback = NULL;
58
59
	/**
60
	 * @var array
61
	 */
62
	protected $sort;
63
64
	/**
65
	 * @var bool
66
	 */
67
	protected $template_escaping = TRUE;
68
69
	/**
70
	 * @var bool
71
	 */
72
	protected $header_escaping = FALSE;
73
74
	
75
	/**
76
	 * @var string
77
	 */
78
	protected $align;
79
80
81
	/**
82
	 * @var array
83
	 */
84
	protected $template_variables = [];
85
86
	/**
87
	 * @var callable
88
	 */
89
	protected $editable_callback;
90
91
	/**
92
	 * @var array
93
	 */
94
	protected $editable_element = ['textarea', ['class' => 'form-control']];
95
96
	/**
97
	 * Cached html elements
98
	 * @var array
99
	 */
100
	protected $el_cache = [];
101
102
	/**
103
	 * @var bool
104
	 */
105
	protected $default_hide = FALSE;
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
	 * Set column translatable or not
220
	 * @param bool $translatable
221
	 */
222
	public function setTranslatable($translatable = TRUE)
223
	{
224
		$this->translatable = (bool) $translatable;
225
226
		return $this;
227
	}
228
229
230
	/**
231
	 * Tell whether column is translatable
232
	 * @return bool
233
	 */
234
	public function isTranslatable()
235
	{
236
		return $this->translatable;
237
	}
238
239
240
	/**
241
	 * Shoud be the pagination reseted after sorting?
242
	 * @param bool $sortable_reset_pagination
243
	 * @return static
244
	 */
245
	public function setSortableResetPagination($sortable_reset_pagination = TRUE)
246
	{
247
		$this->sortable_reset_pagination = (bool) $sortable_reset_pagination;
248
249
		return $this;
250
	}
251
252
253
	/**
254
	 * Do reset pagination after sorting?
255
	 * @return bool
256
	 */
257
	public function sortableResetPagination()
258
	{
259
		return $this->sortable_reset_pagination;
260
	}
261
262
263
	/**
264
	 * Set custom ORDER BY clause
265
	 * @param callable $sortable_callback
266
	 * @return static
267
	 */
268
	public function setSortableCallback(callable $sortable_callback)
269
	{
270
		$this->sortable_callback = $sortable_callback;
271
272
		return $this;
273
	}
274
275
276
	/**
277
	 * Get custom ORDER BY clause
278
	 * @return callable|null
279
	 */
280
	public function getSortableCallback()
281
	{
282
		return $this->sortable_callback;
283
	}
284
285
286
	/**
287
	 * Get column to sort by
288
	 * @return string
289
	 */
290
	public function getSortingColumn()
291
	{
292
		return is_string($this->sortable) ? $this->sortable : $this->column;
293
	}
294
295
296
	/**
297
	 * Get column name
298
	 * @return string
299
	 */
300
	public function getColumnName()
301
	{
302
		return $this->column;
303
	}
304
305
306
	/**
307
	 * Get column value of row item
308
	 * @param  Row   $row
309
	 * @return mixed
310
	 */
311
	public function getColumnValue(Row $row)
312
	{
313
		return $row->getValue($this->column);
314
	}
315
316
317
	/**
318
	 * @return string
319
	 */
320
	public function getName()
321
	{
322
		return $this->name;
323
	}
324
325
326
	/**
327
	 * Set column replacements
328
	 * @param  array $replacements
329
	 * @return Column
330
	 */
331
	public function setReplacement(array $replacements)
332
	{
333
		$this->replacements = $replacements;
334
335
		return $this;
336
	}
337
338
339
	/**
340
	 * Tell whether columns has replacements
341
	 * @return bool
342
	 */
343
	public function hasReplacements()
344
	{
345
		return (bool) $this->replacements;
346
	}
347
348
349
	/**
350
	 * Apply replacements
351
	 * @param  Row   $row
352
	 * @return array
353
	 */
354
	public function applyReplacements(Row $row)
355
	{
356
		$value = $row->getValue($this->column);
357
358
		if ((is_scalar($value) || is_null($value)) && isset($this->replacements[$value])) {
359
			return [TRUE, $this->replacements[$value]];
360
		}
361
362
		return [FALSE, NULL];
363
	}
364
365
366
	/**
367
	 * Set renderer callback and (it may be optional - the condition callback will decide)
368
	 * @param callable $renderer
369
	 */
370
	public function setRenderer($renderer, $condition_callback = NULL)
371
	{
372
		if ($this->hasReplacements()) {
373
			throw new DataGridException(
374
				"Use either Column::setReplacement() or Column::setRenderer, not both."
375
			);
376
		}
377
378
		if (!is_callable($renderer)) {
379
			throw new DataGridException(
380
				"Renderer (method Column::setRenderer()) must be callable."
381
			);
382
		}
383
384
		if (NULL != $condition_callback && !is_callable($condition_callback)) {
385
			throw new DataGridException(
386
				"Renderer (method Column::setRenderer()) must be callable."
387
			);
388
		}
389
390
		$this->renderer = new Renderer($renderer, $condition_callback);
391
392
		return $this;
393
	}
394
395
396
	/**
397
	 * Set renderer callback just if condition is truthy
398
	 * @param callable $renderer
399
	 */
400
	public function setRendererOnCondition($renderer, $condition_callback)
401
	{
402
		return $this->setRenderer($renderer, $condition_callback);
403
	}
404
405
406
	/**
407
	 * Return custom renderer callback
408
	 * @return Renderer|null
409
	 */
410
	public function getRenderer()
411
	{
412
		return $this->renderer;
413
	}
414
415
416
	/**
417
	 * Column may have its own template
418
	 * @param string $template
419
	 */
420
	public function setTemplate($template, array $template_variables = [])
421
	{
422
		$this->template = $template;
423
		$this->template_variables = $template_variables;
424
425
		return $this;
426
	}
427
428
429
	/**
430
	 * Column can have variables that will be passed to custom template scope
431
	 * @return array
432
	 */
433
	public function getTemplateVariables()
434
	{
435
		return $this->template_variables;
436
	}
437
438
439
	/**
440
	 * Tell whether column has its owntemplate
441
	 * @return bool
442
	 */
443
	public function hasTemplate()
444
	{
445
		return (bool) $this->template;
446
	}
447
448
449
	/**
450
	 * Get column template path
451
	 * @return string
452
	 */
453
	public function getTemplate()
454
	{
455
		return $this->template;
456
	}
457
458
459
	/**
460
	 * Tell whether data source is sorted by this collumn
461
	 * @return bool
462
	 */
463
	public function isSortedBy()
464
	{
465
		return (bool) $this->sort;
466
	}
467
468
469
	/**
470
	 * Tell column his sorting options
471
	 * @param array $sort
472
	 */
473
	public function setSort(array $sort)
474
	{
475
		$this->sort = $sort[$this->key];
476
477
		return $this;
478
	}
479
480
481
	/**
482
	 * What sorting will be applied after next click?
483
	 * @return array
484
	 */
485
	public function getSortNext()
486
	{
487
		if ($this->sort == 'ASC') {
488
			return [$this->key => 'DESC'];
489
		} else if ($this->sort == 'DESC') {
490
			return [$this->key => FALSE];
491
		}
492
493
		return [$this->key => 'ASC'];
494
	}
495
496
497
	/**
498
	 * @return bool
499
	 */
500
	public function hasSortNext()
501
	{
502
		foreach ($this->getSortNext() as $key => $order) {
503
			return $order !== FALSE;
504
		}
505
	}
506
507
508
	/**
509
	 * Is sorting ascending?
510
	 * @return bool
511
	 */
512
	public function isSortAsc()
513
	{
514
		return $this->sort == 'ASC';
515
	}
516
517
518
	/**
519
	 * Set column alignment
520
	 * @param string $align
521
	 */
522
	public function setAlign($align)
523
	{
524
		$this->align = (string) $align;
525
526
		return $this;
527
	}
528
529
530
	/**
531
	 * Has column some alignment?
532
	 * @return bool [description]
533
	 */
534
	public function hasAlign()
535
	{
536
		return (bool) $this->align;
537
	}
538
539
540
	/**
541
	 * Get column alignment
542
	 * @return string
543
	 */
544
	public function getAlign()
545
	{
546
		return $this->align ?: 'left';
547
	}
548
549
550
	/**
551
	 * Set column content fit
552
	 * @param bool $fit_content
553
	 * @return $this
554
	 */
555
	public function setFitContent($fit_content = TRUE)
556
	{
557
		($fit_content) ? $this->addAttributes(['class' => 'datagrid-fit-content']) : NULL;
558
559
		return $this;
560
	}
561
562
563
	/**
564
	 * Set callback that will be called after inline editing
565
	 * @param callable $editable_callback
566
	 */
567
	public function setEditableCallback(callable $editable_callback)
568
	{
569
		$this->editable_callback = $editable_callback;
570
571
		return $this;
572
	}
573
574
575
	/**
576
	 * Return callback that is used after inline editing
577
	 * @return callable
578
	 */
579
	public function getEditableCallback()
580
	{
581
		return $this->editable_callback;
582
	}
583
584
585
	/**
586
	 * Is column editable?
587
	 * @return bool
588
	 */
589
	public function isEditable()
590
	{
591
		return (bool) $this->getEditableCallback();
592
	}
593
594
595
	/**
596
	 * Element is by default textarea, user can change that
597
	 * @param string $el_type
598
	 * @param array  $attrs
599
	 * @return static
600
	 */
601
	public function setEditableInputType($el_type, array $attrs = [])
602
	{
603
		$this->editable_element = [$el_type, $attrs];
604
605
		return $this;
606
	}
607
608
609
	/**
610
	 * Change small inline edit input type to select
611
	 * @param array  $options
612
	 * @return static
613
	 */
614
	public function setEditableInputTypeSelect(array $options = [])
615
	{
616
		$select = Html::el('select');
617
618
		foreach ($options as $value => $text) {
619
			$select->create('option')
620
				->value($value)
621
				->setText($text);
622
		}
623
624
		$this->addAttributes(['data-datagrid-editable-element' => (string) $select]);
625
626
		return $this->setEditableInputType('select');
627
	}
628
629
630
	/**
631
	 * [getEditableInputType description]
632
	 * @return array
633
	 */
634
	public function getEditableInputType()
635
	{
636
		return $this->editable_element;
637
	}
638
639
640
	/**
641
	 * Set attributes for both th and td element
642
	 * @param array $attrs
643
	 * @return static
644
	 */
645
	public function addAttributes(array $attrs)
646
	{
647
		$this->getElementPrototype('td')->addAttributes($attrs);
648
		$this->getElementPrototype('th')->addAttributes($attrs);
649
650
		return $this;
651
	}
652
653
654
	/**
655
	 * Get th/td column element
656
	 * @param  string   $tag th|td
657
	 * @param  string   $key
658
	 * @param  Row|NULL $row
659
	 * @return Html
660
	 */
661
	public function getElementPrototype($tag, $key = NULL, Row $row = NULL)
662
	{
663
		/**
664
		 * Get cached element
665
		 */
666
		if (empty($this->el_cache[$tag])) {
667
			$this->el_cache[$tag] = $el = $el = Html::el($tag);
668
		} else {
669
			$el = $this->el_cache[$tag];
670
		}
671
672
		/**
673
		 * If class was set by user via $el->class = '', fix it
674
		 */
675
		if (!empty($el->class) && is_string($el->class)) {
676
			$class = $el->class;
677
			unset($el->class);
678
679
			$el->class[] = $class;
680
		}
681
682
		$el->class[] = "text-{$this->getAlign()}";
683
684
		/**
685
		 * Method called from datagrid template, set appropriate classes and another attributes
686
		 */
687
		if ($key !== NULL && $row !== NULL) {
688
			$el->class[] = "col-{$key}";
689
690
			if ($tag == 'td') {
691
				if ($this->isEditable()) {
692
					$link = $this->grid->link('edit!', ['key' => $key, 'id' => $row->getId()]);
693
694
					$el->data('datagrid-editable-url', $link);
695
696
					$el->data('datagrid-editable-type', $this->editable_element[0]);
697
					$el->data('datagrid-editable-attrs', json_encode($this->editable_element[1]));
698
				}
699
			}
700
		}
701
702
		return $el;
703
	}
704
705
706
	/**
707
	 * @param bool $default_hide
708
	 * @return static
709
	 */
710
	public function setDefaultHide($default_hide = TRUE)
711
	{
712
		$this->default_hide = (bool) $default_hide;
713
714
		if ($default_hide) {
715
			$this->grid->setSomeColumnDefaultHide($default_hide);
716
		}
717
718
		return $this;
719
	}
720
721
722
	public function getDefaultHide()
723
	{
724
		return $this->default_hide;
725
	}
726
727
728
	/**
729
	 * Get row item params (E.g. action may be called id => $item->id, name => $item->name, ...)
730
	 * @param  Row   $row
731
	 * @param  array $params_list
732
	 * @return array
733
	 */
734
	protected function getItemParams(Row $row, array $params_list)
735
	{
736
		$return = [];
737
738
		foreach ($params_list as $param_name => $param) {
739
			$return[is_string($param_name) ? $param_name : $param] = $row->getValue($param);
740
		}
741
742
		return $return;
743
	}
744
745
746
	/**
747
	 * @return string
748
	 */
749
	public function getColumn()
750
	{
751
		return $this->column;
752
	}
753
754
}
755