Completed
Pull Request — master (#321)
by
unknown
02:56
created

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