Completed
Push — master ( f2fb45...cb204b )
by Pavel
03:36
created

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