Completed
Pull Request — master (#315)
by Martin
10:03
created

DataGrid::getColumnsSummary()   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;
10
11
use Nette;
12
use Nette\Application\UI\Link;
13
use Nette\Application\UI\PresenterComponent;
14
use Ublaboo\DataGrid\Utils\ArraysHelper;
15
use Nette\Application\UI\Form;
16
use Ublaboo\DataGrid\Exception\DataGridException;
17
use Ublaboo\DataGrid\Exception\DataGridHasToBeAttachedToPresenterComponentException;
18
use Ublaboo\DataGrid\Utils\Sorting;
19
use Ublaboo\DataGrid\InlineEdit\InlineEdit;
20
use Ublaboo\DataGrid\ColumnsSummary;
21
use Ublaboo\DataGrid\Toolbar\ToolbarButton;
22
23
/**
24
 * @method onRedraw()
25
 * @method onRender()
26
 * @method onColumnAdd()
27
 */
28
class DataGrid extends Nette\Application\UI\Control
29
{
30
31
	/**
32
	 * @var callable[]
33
	 */
34
	public $onRedraw;
35
36
	/**
37
	 * @var callable[]
38
	 */
39
	public $onRender = [];
40
41
	/**
42
	 * @var callable[]
43
	 */
44
	public $onExport;
45
46
	/**
47
	 * @var callable[]
48
	 */
49
	public $onColumnAdd;
50
51
	/**
52
	 * @var string
53
	 */
54
	public static $icon_prefix = 'fa fa-';
55
56
	/**
57
	 * Default form method
58
	 * @var string
59
	 */
60
	public static $form_method = 'post';
61
62
	/**
63
	 * When set to TRUE, datagrid throws an exception
64
	 * 	when tring to get related entity within join and entity does not exist
65
	 * @var bool
66
	 */
67
	public $strict_entity_property = FALSE;
68
69
	/**
70
	 * When set to TRUE, datagrid throws an exception
71
	 * 	when tring to set filter value, that does not exist (select, multiselect, etc)
72
	 * @var bool
73
	 */
74
	public $strict_session_filter_values = TRUE;
75
76
	/**
77
	 * @var int
78
	 * @persistent
79
	 */
80
	public $page = 1;
81
82
	/**
83
	 * @var int|string
84
	 * @persistent
85
	 */
86
	public $per_page;
87
88
	/**
89
	 * @var array
90
	 * @persistent
91
	 */
92
	public $sort = [];
93
94
	/**
95
	 * @var array
96
	 */
97
	public $default_sort = [];
98
99
	/**
100
	 * @var array
101
	 */
102
	public $default_filter = [];
103
104
	/**
105
	 * @var bool
106
	 */
107
	public $default_filter_use_on_reset = TRUE;
108
109
	/**
110
	 * @var array
111
	 * @persistent
112
	 */
113
	public $filter = [];
114
115
	/**
116
	 * @var callable|null
117
	 */
118
	protected $sort_callback = NULL;
119
120
	/**
121
	 * @var bool
122
	 */
123
	protected $use_happy_components = TRUE;
124
125
	/**
126
	 * @var callable
127
	 */
128
	protected $rowCallback;
129
130
	/**
131
	 * @var array
132
	 */
133
	protected $items_per_page_list;
134
135
	/**
136
	 * @var int
137
	 */
138
	protected $default_per_page;
139
140
	/**
141
	 * @var string
142
	 */
143
	protected $template_file;
144
145
	/**
146
	 * @var Column\IColumn[]
147
	 */
148
	protected $columns = [];
149
150
	/**
151
	 * @var Column\Action[]
152
	 */
153
	protected $actions = [];
154
155
	/**
156
	 * @var GroupAction\GroupActionCollection
157
	 */
158
	protected $group_action_collection;
159
160
	/**
161
	 * @var Filter\Filter[]
162
	 */
163
	protected $filters = [];
164
165
	/**
166
	 * @var Export\Export[]
167
	 */
168
	protected $exports = [];
169
170
	/**
171
	 * @var ToolbarButton[]
172
	 */
173
	protected $toolbar_buttons = [];
174
175
	/**
176
	 * @var DataModel
177
	 */
178
	protected $dataModel;
179
180
	/**
181
	 * @var DataFilter
182
	 */
183
	protected $dataFilter;
184
185
	/**
186
	 * @var string
187
	 */
188
	protected $primary_key = 'id';
189
190
	/**
191
	 * @var bool
192
	 */
193
	protected $do_paginate = TRUE;
194
195
	/**
196
	 * @var bool
197
	 */
198
	protected $csv_export = TRUE;
199
200
	/**
201
	 * @var bool
202
	 */
203
	protected $csv_export_filtered = TRUE;
204
205
	/**
206
	 * @var bool
207
	 */
208
	protected $sortable = FALSE;
209
210
	/**
211
	 * @var string
212
	 */
213
	protected $sortable_handler = 'sort!';
214
215
	/**
216
	 * @var string
217
	 */
218
	protected $original_template;
219
220
	/**
221
	 * @var array
222
	 */
223
	protected $redraw_item;
224
225
	/**
226
	 * @var mixed
227
	 */
228
	protected $translator;
229
230
	/**
231
	 * @var bool
232
	 */
233
	protected $force_filter_active;
234
235
	/**
236
	 * @var callable
237
	 */
238
	protected $tree_view_children_callback;
239
240
	/**
241
	 * @var callable
242
	 */
243
	protected $tree_view_has_children_callback;
244
245
	/**
246
	 * @var string
247
	 */
248
	protected $tree_view_has_children_column;
249
250
	/**
251
	 * @var bool
252
	 */
253
	protected $outer_filter_rendering = FALSE;
254
255
	/**
256
	 * @var array
257
	 */
258
	protected $columns_export_order = [];
259
260
	/**
261
	 * @var bool
262
	 */
263
	protected $remember_state = TRUE;
264
265
	/**
266
	 * @var bool
267
	 */
268
	protected $refresh_url = TRUE;
269
270
	/**
271
	 * @var Nette\Http\SessionSection
272
	 */
273
	protected $grid_session;
274
275
	/**
276
	 * @var Column\ItemDetail
277
	 */
278
	protected $items_detail;
279
280
	/**
281
	 * @var array
282
	 */
283
	protected $row_conditions = [
284
		'group_action' => FALSE,
285
		'action' => []
286
	];
287
288
	/**
289
	 * @var array
290
	 */
291
	protected $column_callbacks = [];
292
293
	/**
294
	 * @var bool
295
	 */
296
	protected $can_hide_columns = FALSE;
297
298
	/**
299
	 * @var array
300
	 */
301
	protected $columns_visibility = [];
302
303
	/**
304
	 * @var InlineEdit
305
	 */
306
	protected $inlineEdit;
307
308
	/**
309
	 * @var InlineEdit
310
	 */
311
	protected $inlineAdd;
312
313
	/**
314
	 * @var bool
315
	 */
316
	protected $snippets_set = FALSE;
317
318
	/**
319
	 * @var bool
320
	 */
321
	protected $some_column_default_hide = FALSE;
322
323
	/**
324
	 * @var ColumnsSummary
325
	 */
326
	protected $columnsSummary;
327
328
	/**
329
	 * @var bool
330
	 */
331
	protected $auto_submit = TRUE;
332
333
	/**
334
	 * @var Filter\SubmitButton|NULL
335
	 */
336
	protected $filter_submit_button = NULL;
337
338
339
	/**
340
	 * @param Nette\ComponentModel\IContainer|NULL $parent
341
	 * @param string                               $name
342
	 */
343
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
344
	{
345
		parent::__construct($parent, $name);
346
347
		$this->monitor('Nette\Application\UI\Presenter');
348
349
		/**
350
		 * Try to find previous filters, pagination, per_page and other values in session
351
		 */
352
		$this->onRender[] = [$this, 'findSessionValues'];
353
		$this->onExport[] = [$this, 'findSessionValues'];
354
355
		/**
356
		 * Find default filter values
357
		 */
358
		$this->onRender[] = [$this, 'findDefaultFilter'];
359
		$this->onExport[] = [$this, 'findDefaultFilter'];
360
361
		/**
362
		 * Find default sort
363
		 */
364
		$this->onRender[] = [$this, 'findDefaultSort'];
365
		$this->onExport[] = [$this, 'findDefaultSort'];
366
367
		/**
368
		 * Find default items per page
369
		 */
370
		$this->onRender[] = [$this, 'findDefaultPerPage'];
371
	}
372
373
374
	/**
375
	 * {inheritDoc}
376
	 * @return void
377
	 */
378
	public function attached($presenter)
379
	{
380
		parent::attached($presenter);
381
382
		if ($presenter instanceof Nette\Application\UI\Presenter) {
383
			/**
384
			 * Get session
385
			 */
386
			if ($this->remember_state) {
387
				$this->grid_session = $presenter->getSession($this->getSessionSectionName());
0 ignored issues
show
Documentation Bug introduced by
It seems like $presenter->getSession($...etSessionSectionName()) can also be of type object<Nette\Http\Session>. However, the property $grid_session is declared as type object<Nette\Http\SessionSection>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
388
			}
389
		}
390
	}
391
392
393
	/********************************************************************************
394
	 *                                  RENDERING                                   *
395
	 ********************************************************************************/
396
397
398
	/**
399
	 * Render template
400
	 * @return void
401
	 */
402
	public function render()
403
	{
404
		/**
405
		 * Check whether datagrid has set some columns, initiated data source, etc
406
		 */
407
		if (!($this->dataModel instanceof DataModel)) {
408
			throw new DataGridException('You have to set a data source first.');
409
		}
410
411
		if (empty($this->columns)) {
412
			throw new DataGridException('You have to add at least one column.');
413
		}
414
415
		$this->getTemplate()->setTranslator($this->getTranslator());
416
417
		/**
418
		 * Invoke possible events
419
		 */
420
		$this->onRender($this);
0 ignored issues
show
Unused Code introduced by
The call to DataGrid::onRender() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
421
422
		/**
423
		 * Prepare data for rendering (datagrid may render just one item)
424
		 */
425
		$rows = [];
426
427
		if (!empty($this->redraw_item)) {
428
			$items = $this->dataModel->filterRow($this->redraw_item);
429
		} else {
430
			$items = Nette\Utils\Callback::invokeArgs(
431
				[$this->dataModel, 'filterData'],
432
				[
433
					$this->getPaginator(),
434
					$this->createSorting($this->sort, $this->sort_callback),
435
					$this->assableFilters()
436
				]
437
			);
438
		}
439
440
		$callback = $this->rowCallback ?: NULL;
441
		$hasGroupActionOnRows = FALSE;
442
443
		foreach ($items as $item) {
444
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
445
446
			if (!$hasGroupActionOnRows && $row->hasGroupAction()){
447
				$hasGroupActionOnRows = TRUE;
448
			}
449
			
450
			if ($callback) {
451
				$callback($item, $row->getControl());
452
			}
453
454
			/**
455
			 * Walkaround for item snippet - snippet is the <tr> element and its class has to be also updated
456
			 */
457
			if (!empty($this->redraw_item)) {
458
				$this->getPresenter()->payload->_datagrid_redraw_item_class = $row->getControlClass();
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
459
				$this->getPresenter()->payload->_datagrid_redraw_item_id = $row->getId();
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
460
			}
461
		}
462
463
		if ($hasGroupActionOnRows){
464
			$hasGroupActionOnRows = $this->hasGroupActions();
465
		}
466
467
		if ($this->isTreeView()) {
468
			$this->getTemplate()->add('tree_view_has_children_column', $this->tree_view_has_children_column);
469
		}
470
471
		$this->getTemplate()->add('rows', $rows);
472
473
		$this->getTemplate()->add('columns', $this->getColumns());
474
		$this->getTemplate()->add('actions', $this->actions);
475
		$this->getTemplate()->add('exports', $this->exports);
476
		$this->getTemplate()->add('filters', $this->filters);
477
		$this->getTemplate()->add('toolbar_buttons', $this->toolbar_buttons);
478
479
		$this->getTemplate()->add('filter_active', $this->isFilterActive());
480
		$this->getTemplate()->add('original_template', $this->getOriginalTemplateFile());
481
		//$this->getTemplate()->add('icon_prefix', static::$icon_prefix);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
482
		$this->getTemplate()->icon_prefix = static::$icon_prefix;
0 ignored issues
show
Bug introduced by
Accessing icon_prefix on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
483
		$this->getTemplate()->add('items_detail', $this->items_detail);
484
		$this->getTemplate()->add('columns_visibility', $this->getColumnsVisibility());
485
		$this->getTemplate()->add('columnsSummary', $this->columnsSummary);
486
487
		$this->getTemplate()->add('inlineEdit', $this->inlineEdit);
488
		$this->getTemplate()->add('inlineAdd', $this->inlineAdd);
489
490
		$this->getTemplate()->add('hasGroupActionOnRows', $hasGroupActionOnRows);
491
492
		/**
493
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
494
		 */
495
		$this->getTemplate()->add('filter', $this['filter']);
496
497
		/**
498
		 * Set template file and render it
499
		 */
500
		$this->getTemplate()->setFile($this->getTemplateFile());
501
		$this->getTemplate()->render();
502
	}
503
504
505
	/********************************************************************************
506
	 *                                 ROW CALLBACK                                 *
507
	 ********************************************************************************/
508
509
510
	/**
511
	 * Each row can be modified with user callback
512
	 * @param  callable  $callback
513
	 * @return static
514
	 */
515
	public function setRowCallback(callable $callback)
516
	{
517
		$this->rowCallback = $callback;
518
519
		return $this;
520
	}
521
522
523
	/********************************************************************************
524
	 *                                 DATA SOURCE                                  *
525
	 ********************************************************************************/
526
527
528
	/**
529
	 * By default ID, you can change that
530
	 * @param string $primary_key
531
	 * @return static
532
	 */
533
	public function setPrimaryKey($primary_key)
534
	{
535
		if ($this->dataModel instanceof DataModel) {
536
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
537
		}
538
539
		$this->primary_key = $primary_key;
540
541
		return $this;
542
	}
543
544
545
	/**
546
	 * Set Grid data source
547
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
548
	 * @return static
549
	 */
550
	public function setDataSource($source)
551
	{
552
		$this->dataModel = new DataModel($source, $this->primary_key);
553
554
		return $this;
555
	}
556
557
558
	/**
559
	 * @return DataSource\IDataSource|NULL
560
	 */
561
	public function getDataSource()
562
	{
563
		if (!$this->dataModel) {
564
			return NULL;
565
		}
566
567
		return $this->dataModel->getDataSource();
568
	}
569
570
571
	/********************************************************************************
572
	 *                                  TEMPLATING                                  *
573
	 ********************************************************************************/
574
575
576
	/**
577
	 * Set custom template file to render
578
	 * @param string $template_file
579
	 * @return static
580
	 */
581
	public function setTemplateFile($template_file)
582
	{
583
		$this->template_file = $template_file;
584
585
		return $this;
586
	}
587
588
589
	/**
590
	 * Get DataGrid template file
591
	 * @return string
592
	 * @return static
593
	 */
594
	public function getTemplateFile()
595
	{
596
		return $this->template_file ?: $this->getOriginalTemplateFile();
597
	}
598
599
600
	/**
601
	 * Get DataGrid original template file
602
	 * @return string
603
	 */
604
	public function getOriginalTemplateFile()
605
	{
606
		return __DIR__.'/templates/datagrid.latte';
607
	}
608
609
610
	/**
611
	 * Tell datagrid wheteher to use or not happy components
612
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
613
	 * @return void|bool
614
	 */
615
	public function useHappyComponents($use = NULL)
616
	{
617
		if (NULL === $use) {
618
			return $this->use_happy_components;
619
		}
620
621
		$this->use_happy_components = (bool) $use;
622
	}
623
624
625
	/********************************************************************************
626
	 *                                   SORTING                                    *
627
	 ********************************************************************************/
628
629
630
	/**
631
	 * Set default sorting
632
	 * @param array $sort
633
	 * @return static
634
	 */
635
	public function setDefaultSort($sort)
636
	{
637
		if (is_string($sort)) {
638
			$sort = [$sort => 'ASC'];
639
		} else {
640
			$sort = (array) $sort;
641
		}
642
643
		$this->default_sort = $sort;
644
645
		return $this;
646
	}
647
648
649
	/**
650
	 * User may set default sorting, apply it
651
	 * @return void
652
	 */
653
	public function findDefaultSort()
654
	{
655
		if (!empty($this->sort)) {
656
			return;
657
		}
658
659
		if (!empty($this->default_sort)) {
660
			$this->sort = $this->default_sort;
661
		}
662
663
		$this->saveSessionData('_grid_sort', $this->sort);
664
	}
665
666
667
	/**
668
	 * Set grido to be sortable
669
	 * @param bool $sortable
670
	 * @return static
671
	 */
672
	public function setSortable($sortable = TRUE)
673
	{
674
		if ($this->getItemsDetail()) {
675
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
676
		}
677
678
		$this->sortable = (bool) $sortable;
679
680
		return $this;
681
	}
682
683
684
	/**
685
	 * Set sortable handle
686
	 * @param string $handler
687
	 * @return static
688
	 */
689
	public function setSortableHandler($handler = 'sort!')
690
	{
691
		$this->sortable_handler = (string) $handler;
692
693
		return $this;
694
	}
695
696
697
	/**
698
	 * Tell whether DataGrid is sortable
699
	 * @return bool
700
	 */
701
	public function isSortable()
702
	{
703
		return $this->sortable;
704
	}
705
706
	/**
707
	 * Return sortable handle name
708
	 * @return string
709
	 */
710
	public function getSortableHandler()
711
	{
712
		return $this->sortable_handler;
713
	}
714
715
716
	protected function createSorting(array $sort, $sort_callback)
717
	{
718
		foreach ($sort as $key => $order) {
719
			$column = $this->getColumn($key);
720
			$sort = [$column->getSortingColumn() => $order];
721
		}
722
723
		return new Sorting($sort, $sort_callback);
724
	}
725
726
727
	/********************************************************************************
728
	 *                                  TREE VIEW                                   *
729
	 ********************************************************************************/
730
731
732
	/**
733
	 * Is tree view set?
734
	 * @return boolean
735
	 */
736
	public function isTreeView()
737
	{
738
		return (bool) $this->tree_view_children_callback;
739
	}
740
741
742
	/**
743
	 * Setting tree view
744
	 * @param callable $get_children_callback
745
	 * @param string|callable $tree_view_has_children_column
746
	 * @return static
747
	 */
748
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
749
	{
750
		if (!is_callable($get_children_callback)) {
751
			throw new DataGridException(
752
				'Parameters to method DataGrid::setTreeView must be of type callable'
753
			);
754
		}
755
756
		if (is_callable($tree_view_has_children_column)) {
757
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
758
			$tree_view_has_children_column = NULL;
759
		}
760
761
		$this->tree_view_children_callback = $get_children_callback;
762
		$this->tree_view_has_children_column = $tree_view_has_children_column;
763
764
		/**
765
		 * TUrn off pagination
766
		 */
767
		$this->setPagination(FALSE);
768
769
		/**
770
		 * Set tree view template file
771
		 */
772
		if (!$this->template_file) {
773
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
774
		}
775
776
		return $this;
777
	}
778
779
780
	/**
781
	 * Is tree view children callback set?
782
	 * @return boolean
783
	 */
784
	public function hasTreeViewChildrenCallback()
785
	{
786
		return is_callable($this->tree_view_has_children_callback);
787
	}
788
789
790
	/**
791
	 * @param  mixed $item
792
	 * @return boolean
793
	 */
794
	public function treeViewChildrenCallback($item)
795
	{
796
		return call_user_func($this->tree_view_has_children_callback, $item);
797
	}
798
799
800
	/********************************************************************************
801
	 *                                    COLUMNS                                   *
802
	 ********************************************************************************/
803
804
805
	/**
806
	 * Add text column with no other formating
807
	 * @param  string      $key
808
	 * @param  string      $name
809
	 * @param  string|null $column
810
	 * @return Column\ColumnText
811
	 */
812
	public function addColumnText($key, $name, $column = NULL)
813
	{
814
		$this->addColumnCheck($key);
815
		$column = $column ?: $key;
816
817
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
818
	}
819
820
821
	/**
822
	 * Add column with link
823
	 * @param  string      $key
824
	 * @param  string      $name
825
	 * @param  string|null $column
826
	 * @return Column\ColumnLink
827
	 */
828
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
829
	{
830
		$this->addColumnCheck($key);
831
		$column = $column ?: $key;
832
		$href = $href ?: $key;
833
834
		if (NULL === $params) {
835
			$params = [$this->primary_key];
836
		}
837
838
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
839
	}
840
841
842
	/**
843
	 * Add column with possible number formating
844
	 * @param  string      $key
845
	 * @param  string      $name
846
	 * @param  string|null $column
847
	 * @return Column\ColumnNumber
848
	 */
849
	public function addColumnNumber($key, $name, $column = NULL)
850
	{
851
		$this->addColumnCheck($key);
852
		$column = $column ?: $key;
853
854
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
855
	}
856
857
858
	/**
859
	 * Add column with date formating
860
	 * @param  string      $key
861
	 * @param  string      $name
862
	 * @param  string|null $column
863
	 * @return Column\ColumnDateTime
864
	 */
865
	public function addColumnDateTime($key, $name, $column = NULL)
866
	{
867
		$this->addColumnCheck($key);
868
		$column = $column ?: $key;
869
870
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
871
	}
872
873
874
	/**
875
	 * Add column status
876
	 * @param  string      $key
877
	 * @param  string      $name
878
	 * @param  string|null $column
879
	 * @return Column\ColumnStatus
880
	 */
881
	public function addColumnStatus($key, $name, $column = NULL)
882
	{
883
		$this->addColumnCheck($key);
884
		$column = $column ?: $key;
885
886
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
887
	}
888
889
890
	/**
891
	 * @param string $key
892
	 * @param Column\Column $column
893
	 * @return Column\Column
894
	 */
895
	protected function addColumn($key, Column\Column $column)
896
	{
897
		$this->onColumnAdd($key, $column);
0 ignored issues
show
Unused Code introduced by
The call to DataGrid::onColumnAdd() has too many arguments starting with $key.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
898
899
		$this->columns_visibility[$key] = [
900
			'visible' => TRUE
901
		];
902
903
		return $this->columns[$key] = $column;
904
	}
905
906
907
	/**
908
	 * Return existing column
909
	 * @param  string $key
910
	 * @return Column\Column
911
	 * @throws DataGridException
912
	 */
913
	public function getColumn($key)
914
	{
915
		if (!isset($this->columns[$key])) {
916
			throw new DataGridException("There is no column at key [$key] defined.");
917
		}
918
919
		return $this->columns[$key];
920
	}
921
922
923
	/**
924
	 * Remove column
925
	 * @param string $key
926
	 * @return void
927
	 */
928
	public function removeColumn($key)
929
	{
930
		unset($this->columns[$key]);
931
	}
932
933
934
	/**
935
	 * Check whether given key already exists in $this->columns
936
	 * @param  string $key
937
	 * @throws DataGridException
938
	 */
939
	protected function addColumnCheck($key)
940
	{
941
		if (isset($this->columns[$key])) {
942
			throw new DataGridException("There is already column at key [$key] defined.");
943
		}
944
	}
945
946
947
	/********************************************************************************
948
	 *                                    ACTIONS                                   *
949
	 ********************************************************************************/
950
951
952
	/**
953
	 * Create action
954
	 * @param string     $key
955
	 * @param string     $name
956
	 * @param string     $href
957
	 * @param array|null $params
958
	 * @return Column\Action
959
	 */
960
	public function addAction($key, $name, $href = NULL, array $params = NULL)
961
	{
962
		$this->addActionCheck($key);
963
		$href = $href ?: $key;
964
965
		if (NULL === $params) {
966
			$params = [$this->primary_key];
967
		}
968
969
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
970
	}
971
972
973
	/**
974
	 * Create action callback
975
	 * @param string     $key
976
	 * @param string     $name
977
	 * @return Column\Action
978
	 */
979
	public function addActionCallback($key, $name, $callback = NULL)
980
	{
981
		$this->addActionCheck($key);
982
		$params = ['__id' => $this->primary_key];
983
984
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
985
986
		if ($callback) {
987
			if (!is_callable($callback)) {
988
				throw new DataGridException('ActionCallback callback has to be callable.');
989
			}
990
991
			$action->onClick[] = $callback;
992
		}
993
994
		return $action;
995
	}
996
997
998
	/**
999
	 * Get existing action
1000
	 * @param  string       $key
1001
	 * @return Column\Action
1002
	 * @throws DataGridException
1003
	 */
1004
	public function getAction($key)
1005
	{
1006
		if (!isset($this->actions[$key])) {
1007
			throw new DataGridException("There is no action at key [$key] defined.");
1008
		}
1009
1010
		return $this->actions[$key];
1011
	}
1012
1013
1014
	/**
1015
	 * Remove action
1016
	 * @param string $key
1017
	 * @return void
1018
	 */
1019
	public function removeAction($key)
1020
	{
1021
		unset($this->actions[$key]);
1022
	}
1023
1024
1025
	/**
1026
	 * Check whether given key already exists in $this->filters
1027
	 * @param  string $key
1028
	 * @throws DataGridException
1029
	 */
1030
	protected function addActionCheck($key)
1031
	{
1032
		if (isset($this->actions[$key])) {
1033
			throw new DataGridException("There is already action at key [$key] defined.");
1034
		}
1035
	}
1036
1037
1038
	/********************************************************************************
1039
	 *                                    FILTERS                                   *
1040
	 ********************************************************************************/
1041
1042
1043
	/**
1044
	 * Add filter fot text search
1045
	 * @param string       $key
1046
	 * @param string       $name
1047
	 * @param array|string $columns
1048
	 * @return Filter\FilterText
1049
	 * @throws DataGridException
1050
	 */
1051
	public function addFilterText($key, $name, $columns = NULL)
1052
	{
1053
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
1054
1055
		if (!is_array($columns)) {
1056
			throw new DataGridException("Filter Text can except only array or string.");
1057
		}
1058
1059
		$this->addFilterCheck($key);
1060
1061
		return $this->filters[$key] = new Filter\FilterText($this, $key, $name, $columns);
1062
	}
1063
1064
1065
	/**
1066
	 * Add select box filter
1067
	 * @param string $key
1068
	 * @param string $name
1069
	 * @param array  $options
1070
	 * @param string $column
1071
	 * @return Filter\FilterSelect
1072
	 * @throws DataGridException
1073
	 */
1074 View Code Duplication
	public function addFilterSelect($key, $name, array $options, $column = NULL)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1075
	{
1076
		$column = $column ?: $key;
1077
1078
		if (!is_string($column)) {
1079
			throw new DataGridException("Filter Select can only filter in one column.");
1080
		}
1081
1082
		$this->addFilterCheck($key);
1083
1084
		return $this->filters[$key] = new Filter\FilterSelect($this, $key, $name, $options, $column);
1085
	}
1086
1087
1088
	/**
1089
	 * Add multi select box filter
1090
	 * @param string $key
1091
	 * @param string $name
1092
	 * @param array  $options
1093
	 * @param string $column
1094
	 * @return Filter\FilterSelect
1095
	 * @throws DataGridException
1096
	 */
1097 View Code Duplication
	public function addFilterMultiSelect($key, $name, array $options, $column = NULL)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1098
	{
1099
		$column = $column ?: $key;
1100
1101
		if (!is_string($column)) {
1102
			throw new DataGridException("Filter MultiSelect can only filter in one column.");
1103
		}
1104
1105
		$this->addFilterCheck($key);
1106
1107
		return $this->filters[$key] = new Filter\FilterMultiSelect($this, $key, $name, $options, $column);
1108
	}
1109
1110
1111
	/**
1112
	 * Add datepicker filter
1113
	 * @param string $key
1114
	 * @param string $name
1115
	 * @param string $column
1116
	 * @return Filter\FilterDate
1117
	 * @throws DataGridException
1118
	 */
1119 View Code Duplication
	public function addFilterDate($key, $name, $column = NULL)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1120
	{
1121
		$column = $column ?: $key;
1122
1123
		if (!is_string($column)) {
1124
			throw new DataGridException("FilterDate can only filter in one column.");
1125
		}
1126
1127
		$this->addFilterCheck($key);
1128
1129
		return $this->filters[$key] = new Filter\FilterDate($this, $key, $name, $column);
1130
	}
1131
1132
1133
	/**
1134
	 * Add range filter (from - to)
1135
	 * @param string $key
1136
	 * @param string $name
1137
	 * @param string $column
1138
	 * @return Filter\FilterRange
1139
	 * @throws DataGridException
1140
	 */
1141 View Code Duplication
	public function addFilterRange($key, $name, $column = NULL, $name_second = '-')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1142
	{
1143
		$column = $column ?: $key;
1144
1145
		if (!is_string($column)) {
1146
			throw new DataGridException("FilterRange can only filter in one column.");
1147
		}
1148
1149
		$this->addFilterCheck($key);
1150
1151
		return $this->filters[$key] = new Filter\FilterRange($this, $key, $name, $column, $name_second);
1152
	}
1153
1154
1155
	/**
1156
	 * Add datepicker filter (from - to)
1157
	 * @param string $key
1158
	 * @param string $name
1159
	 * @param string $column
1160
	 * @return Filter\FilterDateRange
1161
	 * @throws DataGridException
1162
	 */
1163 View Code Duplication
	public function addFilterDateRange($key, $name, $column = NULL, $name_second = '-')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1164
	{
1165
		$column = $column ?: $key;
1166
1167
		if (!is_string($column)) {
1168
			throw new DataGridException("FilterDateRange can only filter in one column.");
1169
		}
1170
1171
		$this->addFilterCheck($key);
1172
1173
		return $this->filters[$key] = new Filter\FilterDateRange($this, $key, $name, $column, $name_second);
1174
	}
1175
1176
1177
	/**
1178
	 * Check whether given key already exists in $this->filters
1179
	 * @param  string $key
1180
	 * @throws DataGridException
1181
	 */
1182
	protected function addFilterCheck($key)
1183
	{
1184
		if (isset($this->filters[$key])) {
1185
			throw new DataGridException("There is already action at key [$key] defined.");
1186
		}
1187
	}
1188
1189
1190
	/**
1191
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1192
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1193
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1194
	 */
1195
	public function assableFilters()
1196
	{
1197
		foreach ($this->filter as $key => $value) {
1198
			if (!isset($this->filters[$key])) {
1199
				$this->deleteSesssionData($key);
1200
1201
				continue;
1202
			}
1203
1204
			if (is_array($value) || $value instanceof \Traversable) {
1205
				if (!ArraysHelper::testEmpty($value)) {
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type object<Traversable>; however, Ublaboo\DataGrid\Utils\ArraysHelper::testEmpty() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1206
					$this->filters[$key]->setValue($value);
1207
				}
1208
			} else {
1209
				if ($value !== '' && $value !== NULL) {
1210
					$this->filters[$key]->setValue($value);
1211
				}
1212
			}
1213
		}
1214
1215
		foreach ($this->columns as $key => $column) {
1216
			if (isset($this->sort[$key])) {
1217
				$column->setSort($this->sort);
1218
			}
1219
		}
1220
1221
		return $this->filters;
1222
	}
1223
1224
1225
	/**
1226
	 * Remove filter
1227
	 * @param string $key
1228
	 * @return void
1229
	 */
1230
	public function removeFilter($key)
1231
	{
1232
		unset($this->filters[$key]);
1233
	}
1234
1235
1236
	/**
1237
	 * Get defined filter
1238
	 * @param  string $key
1239
	 * @return Filter\Filter
1240
	 */
1241
	public function getFilter($key)
1242
	{
1243
		if (!isset($this->filters[$key])) {
1244
			throw new DataGridException("Filter [{$key}] is not defined");
1245
		}
1246
1247
		return $this->filters[$key];
1248
	}
1249
1250
1251
	/**
1252
	 * @param bool $strict
1253
	 */
1254
	public function setStrictSessionFilterValues($strict = TRUE)
1255
	{
1256
		$this->strict_session_filter_values = (bool) $strict;
1257
	}
1258
1259
1260
	/********************************************************************************
1261
	 *                                  FILTERING                                   *
1262
	 ********************************************************************************/
1263
1264
1265
	/**
1266
	 * Is filter active?
1267
	 * @return boolean
1268
	 */
1269
	public function isFilterActive()
1270
	{
1271
		$is_filter = ArraysHelper::testTruthy($this->filter);
1272
1273
		return ($is_filter) || $this->force_filter_active;
1274
	}
1275
1276
1277
	/**
1278
	 * Tell that filter is active from whatever reasons
1279
	 * return static
1280
	 */
1281
	public function setFilterActive()
1282
	{
1283
		$this->force_filter_active = TRUE;
1284
1285
		return $this;
1286
	}
1287
1288
1289
	/**
1290
	 * Set filter values (force - overwrite user data)
1291
	 * @param array $filter
1292
	 * @return static
1293
	 */
1294
	public function setFilter(array $filter)
1295
	{
1296
		$this->filter = $filter;
1297
1298
		$this->saveSessionData('_grid_has_filtered', 1);
1299
1300
		return $this;
1301
	}
1302
1303
1304
	/**
1305
	 * If we want to sent some initial filter
1306
	 * @param array $filter
0 ignored issues
show
Documentation introduced by
There is no parameter named $filter. Did you maybe mean $default_filter?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1307
	 * @param bool  $use_on_reset
1308
	 * @return static
1309
	 */
1310
	public function setDefaultFilter(array $default_filter, $use_on_reset = TRUE)
1311
	{
1312
		foreach ($default_filter as $key => $value) {
1313
			$filter = $this->getFilter($key);
1314
1315
			if (!$filter) {
1316
				throw new DataGridException("Can not set default value to nonexisting filter [$key]");
1317
			}
1318
1319
			if ($filter instanceof Filter\FilterMultiSelect && !is_array($value)) {
1320
				throw new DataGridException(
1321
					"Default value of filter [$key] - MultiSelect has to be an array"
1322
				);
1323
			}
1324
1325
			if ($filter instanceof Filter\FilterRange || $filter instanceof Filter\FilterDateRange) {
1326
				if (!is_array($value)) {
1327
					throw new DataGridException(
1328
						"Default value of filter [$key] - Range/DateRange has to be an array [from/to => ...]"
1329
					);
1330
				}
1331
1332
				$temp = $value;
1333
				unset($temp['from'], $temp['to']);
1334
1335
				if (!empty($temp)) {
1336
					throw new DataGridException(
1337
						"Default value of filter [$key] - Range/DateRange can contain only [from/to => ...] values"
1338
					);
1339
				}
1340
			}
1341
		}
1342
1343
		$this->default_filter = $default_filter;
1344
		$this->default_filter_use_on_reset = (bool) $use_on_reset;
1345
1346
		return $this;
1347
	}
1348
1349
1350
	/**
1351
	 * User may set default filter, find it
1352
	 * @return void
1353
	 */
1354
	public function findDefaultFilter()
1355
	{
1356
		if (!empty($this->filter)) {
1357
			return;
1358
		}
1359
1360
		if ($this->getSessionData('_grid_has_filtered')) {
1361
			return;
1362
		}
1363
1364
		if (!empty($this->default_filter)) {
1365
			$this->filter = $this->default_filter;
1366
		}
1367
1368
		foreach ($this->filter as $key => $value) {
1369
			$this->saveSessionData($key, $value);
1370
		}
1371
	}
1372
1373
1374
	/**
1375
	 * FilterAndGroupAction form factory
1376
	 * @return Form
1377
	 */
1378
	public function createComponentFilter()
1379
	{
1380
		$form = new Form($this, 'filter');
1381
1382
		$form->setMethod(static::$form_method);
1383
1384
		$form->setTranslator($this->getTranslator());
1385
1386
		/**
1387
		 * InlineEdit part
1388
		 */
1389
		$inline_edit_container = $form->addContainer('inline_edit');
1390
1391 View Code Duplication
		if ($this->inlineEdit instanceof InlineEdit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1392
			$inline_edit_container->addSubmit('submit', 'ublaboo_datagrid.save');
1393
			$inline_edit_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1394
				->setValidationScope(FALSE);
1395
1396
			$this->inlineEdit->onControlAdd($inline_edit_container);
1397
			$this->inlineEdit->onControlAfterAdd($inline_edit_container);
1398
		}
1399
1400
		/**
1401
		 * InlineAdd part
1402
		 */
1403
		$inline_add_container = $form->addContainer('inline_add');
1404
1405 View Code Duplication
		if ($this->inlineAdd instanceof InlineEdit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1406
			$inline_add_container->addSubmit('submit', 'ublaboo_datagrid.save');
1407
			$inline_add_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1408
				->setValidationScope(FALSE)
1409
				->setAttribute('data-datagrid-cancel-inline-add', TRUE);
1410
1411
			$this->inlineAdd->onControlAdd($inline_add_container);
1412
			$this->inlineAdd->onControlAfterAdd($inline_add_container);
1413
		}
1414
1415
		/**
1416
		 * ItemDetail form part
1417
		 */
1418
		$items_detail_form = $this->getItemDetailForm();
1419
1420
		if ($items_detail_form instanceof Nette\Forms\Container) {
1421
			$form['items_detail_form'] = $items_detail_form;
1422
		}
1423
1424
		/**
1425
		 * Filter part
1426
		 */
1427
		$filter_container = $form->addContainer('filter');
1428
1429
		foreach ($this->filters as $filter) {
1430
			$filter->addToFormContainer($filter_container);
1431
		}
1432
1433
		if (!$this->hasAutoSubmit()) {
1434
			$filter_container['submit'] = $this->getFilterSubmitButton();
1435
		}
1436
1437
		/**
1438
		 * Group action part
1439
		 */
1440
		$group_action_container = $form->addContainer('group_action');
1441
1442
		if ($this->hasGroupActions()) {
1443
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1444
		}
1445
1446
		$this->setFilterContainerDefaults($form['filter'], $this->filter);
1447
1448
		/**
1449
		 * Per page part
1450
		 */
1451
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1452
			->setTranslator(NULL);
1453
1454
		if (!$form->isSubmitted()) {
1455
			$form['per_page']->setValue($this->getPerPage());
1456
		}
1457
1458
		$form->addSubmit('per_page_submit', '');
1459
		
1460
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1461
1462
		return $form;
1463
	}
1464
1465
1466
	public function setFilterContainerDefaults(Nette\Forms\Container $container, array $values)
1467
	{
1468
		foreach ($container->getComponents() as $name => $control) {
1469
			if ($control instanceof Nette\Forms\IControl) {
1470 View Code Duplication
				if (array_key_exists($name, $values)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1471
					try {
1472
						$control->setValue($values[$name]);
1473
					} catch (Nette\InvalidArgumentException $e) {
1474
						if ($this->strict_session_filter_values) {
1475
							throw $e;
1476
						}
1477
					}
1478
				}
1479
1480
			} elseif ($control instanceof Nette\Forms\Container) {
1481 View Code Duplication
				if (array_key_exists($name, $values)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1482
					try {
1483
						$control->setValues($values[$name]);
1484
					} catch (Nette\InvalidArgumentException $e) {
1485
						if ($this->strict_session_filter_values) {
1486
							throw $e;
1487
						}
1488
					}
1489
				}
1490
			}
1491
		}
1492
	}
1493
1494
1495
	/**
1496
	 * Set $this->filter values after filter form submitted
1497
	 * @param  Form $form
1498
	 * @return void
1499
	 */
1500
	public function filterSucceeded(Form $form)
1501
	{
1502
		if ($this->snippets_set) {
1503
			return;
1504
		}
1505
1506
		$values = $form->getValues();
1507
1508
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1509
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1510
				return;
1511
			}
1512
		}
1513
1514
		/**
1515
		 * Per page
1516
		 */
1517
		$this->saveSessionData('_grid_per_page', $values->per_page);
1518
		$this->per_page = $values->per_page;
1519
1520
		/**
1521
		 * Inline edit
1522
		 */
1523
		if (isset($form['inline_edit']) && isset($form['inline_edit']['submit']) && isset($form['inline_edit']['cancel'])) {
1524
			$edit = $form['inline_edit'];
1525
1526
			if ($edit['submit']->isSubmittedBy() || $edit['cancel']->isSubmittedBy()) {
1527
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1528
				$primary_where_column = $form->getHttpData(
1529
					Form::DATA_LINE,
1530
					'inline_edit[_primary_where_column]'
1531
				);
1532
1533
				if ($edit['submit']->isSubmittedBy()) {
1534
					$this->inlineEdit->onSubmit($id, $values->inline_edit);
1535
					$this->getPresenter()->payload->_datagrid_inline_edited = $id;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1536
				} else {
1537
					$this->getPresenter()->payload->_datagrid_inline_edit_cancel = $id;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1538
				}
1539
1540
				if ($edit['submit']->isSubmittedBy() && !empty($this->inlineEdit->onCustomRedraw)) {
1541
					$this->inlineEdit->onCustomRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onCustomRedraw does not exist on object<Ublaboo\DataGrid\InlineEdit\InlineEdit>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1542
				} else {
1543
					$this->redrawItem($id, $primary_where_column);
1544
				}
1545
1546
				return;
1547
			}
1548
		}
1549
1550
		/**
1551
		 * Inline add
1552
		 */
1553
		if (isset($form['inline_add']) && isset($form['inline_add']['submit']) && isset($form['inline_add']['cancel'])) {
1554
			$add = $form['inline_add'];
1555
1556
			if ($add['submit']->isSubmittedBy() || $add['cancel']->isSubmittedBy()) {
1557
				if ($add['submit']->isSubmittedBy()) {
1558
					$this->inlineAdd->onSubmit($values->inline_add);
0 ignored issues
show
Bug introduced by
The call to onSubmit() misses a required argument $values.

This check looks for function calls that miss required arguments.

Loading history...
1559
1560
					if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1561
						$this->getPresenter()->payload->_datagrid_inline_added = TRUE;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1562
					}
1563
				}
1564
1565
				return;
1566
			}
1567
		}
1568
1569
		/**
1570
		 * Filter itself
1571
		 */
1572
		$values = $values['filter'];
1573
1574
		foreach ($values as $key => $value) {
1575
			/**
1576
			 * Session stuff
1577
			 */
1578
			$this->saveSessionData($key, $value);
1579
1580
			/**
1581
			 * Other stuff
1582
			 */
1583
			$this->filter[$key] = $value;
1584
		}
1585
1586
		if (!empty($values)) {
1587
			$this->saveSessionData('_grid_has_filtered', 1);
1588
		}
1589
1590
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1591
			$this->getPresenter()->payload->_datagrid_sort = [];
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1592
1593
			foreach ($this->columns as $key => $column) {
1594
				if ($column->isSortable()) {
1595
					$this->getPresenter()->payload->_datagrid_sort[$key] = $this->link('sort!', [
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1596
						'sort' => $column->getSortNext()
1597
					]);
1598
				}
1599
			}
1600
		}
1601
1602
		$this->reload();
1603
	}
1604
1605
1606
	/**
1607
	 * Should be datagrid filters rendered separately?
1608
	 * @param boolean $out
1609
	 * @return static
1610
	 */
1611
	public function setOuterFilterRendering($out = TRUE)
1612
	{
1613
		$this->outer_filter_rendering = (bool) $out;
1614
1615
		return $this;
1616
	}
1617
1618
1619
	/**
1620
	 * Are datagrid filters rendered separately?
1621
	 * @return boolean
1622
	 */
1623
	public function hasOuterFilterRendering()
1624
	{
1625
		return $this->outer_filter_rendering;
1626
	}
1627
1628
1629
	/**
1630
	 * Try to restore session stuff
1631
	 * @return void
1632
	 */
1633
	public function findSessionValues()
1634
	{
1635
		if ($this->filter || ($this->page != 1) || !empty($this->sort) || $this->per_page) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filter of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1636
			return;
1637
		}
1638
1639
		if (!$this->remember_state) {
1640
			return;
1641
		}
1642
1643
		if ($page = $this->getSessionData('_grid_page')) {
1644
			$this->page = $page;
1645
		}
1646
1647
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1648
			$this->per_page = $per_page;
1649
		}
1650
1651
		if ($sort = $this->getSessionData('_grid_sort')) {
1652
			$this->sort = $sort;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sort of type * is incompatible with the declared type array of property $sort.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1653
		}
1654
1655
		foreach ($this->getSessionData() as $key => $value) {
1656
			$other_session_keys = [
1657
				'_grid_per_page',
1658
				'_grid_sort',
1659
				'_grid_page',
1660
				'_grid_has_filtered',
1661
				'_grid_hidden_columns',
1662
				'_grid_hidden_columns_manipulated'
1663
			];
1664
1665
			if (!in_array($key, $other_session_keys)) {
1666
				try {
1667
					$this->getFilter($key);
1668
1669
					$this->filter[$key] = $value;
1670
1671
				} catch (DataGridException $e) {
1672
					if ($this->strict_session_filter_values) {
1673
						throw new DataGridException("Session filter: Filter [$key] not found");
1674
					}
1675
				}
1676
			}
1677
		}
1678
1679
		/**
1680
		 * When column is sorted via custom callback, apply it
1681
		 */
1682
		if (empty($this->sort_callback) && !empty($this->sort)) {
1683
			foreach ($this->sort as $key => $order) {
1684
				try {
1685
					$column = $this->getColumn($key);
1686
1687
				} catch (DataGridException $e) {
1688
					$this->deleteSesssionData('_grid_sort');
1689
					$this->sort = [];
1690
1691
					return;
1692
				}
1693
1694
				if ($column && $column->isSortable() && is_callable($column->getSortableCallback())) {
1695
					$this->sort_callback = $column->getSortableCallback();
1696
				}
1697
			}
1698
		}
1699
	}
1700
1701
1702
	/********************************************************************************
1703
	 *                                    EXPORTS                                   *
1704
	 ********************************************************************************/
1705
1706
1707
	/**
1708
	 * Add export of type callback
1709
	 * @param string $text
1710
	 * @param callable $callback
1711
	 * @param boolean $filtered
1712
	 * @return Export\Export
1713
	 */
1714
	public function addExportCallback($text, $callback, $filtered = FALSE)
1715
	{
1716
		if (!is_callable($callback)) {
1717
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1718
		}
1719
1720
		return $this->addToExports(new Export\Export($this, $text, $callback, $filtered));
1721
	}
1722
1723
1724
	/**
1725
	 * Add already implemented csv export
1726
	 * @param string $text
1727
	 * @param string $csv_file_name
1728
	 * @param string|null $output_encoding
1729
	 * @param string|null $delimiter
1730
	 * @param bool $include_bom
1731
	 * @return Export\Export
1732
	 */
1733 View Code Duplication
	public function addExportCsv($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL, $include_bom = FALSE)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1734
	{
1735
		return $this->addToExports(new Export\ExportCsv(
1736
			$this,
1737
			$text,
1738
			$csv_file_name,
1739
			FALSE,
1740
			$output_encoding,
1741
			$delimiter,
1742
			$include_bom
1743
		));
1744
	}
1745
1746
1747
	/**
1748
	 * Add already implemented csv export, but for filtered data
1749
	 * @param string $text
1750
	 * @param string $csv_file_name
1751
	 * @param string|null $output_encoding
1752
	 * @param string|null $delimiter
1753
	 * @param bool $include_bom
1754
	 * @return Export\Export
1755
	 */
1756 View Code Duplication
	public function addExportCsvFiltered($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL, $include_bom = FALSE)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1757
	{
1758
		return $this->addToExports(new Export\ExportCsv(
1759
			$this,
1760
			$text,
1761
			$csv_file_name,
1762
			TRUE,
1763
			$output_encoding,
1764
			$delimiter,
1765
			$include_bom
1766
		));
1767
	}
1768
1769
1770
	/**
1771
	 * Add export to array
1772
	 * @param Export\Export $export
1773
	 * @return Export\Export
1774
	 */
1775
	protected function addToExports(Export\Export $export)
1776
	{
1777
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1778
1779
		$export->setLink(new Link($this, 'export!', ['id' => $id]));
1780
1781
		return $this->exports[$id] = $export;
1782
	}
1783
1784
1785
	public function resetExportsLinks()
1786
	{
1787
		foreach ($this->exports as $id => $export) {
1788
			$export->setLink(new Link($this, 'export!', ['id' => $id]));
1789
		}
1790
	}
1791
1792
1793
	/********************************************************************************
1794
	 *                                TOOLBAR BUTTONS                               *
1795
	 ********************************************************************************/
1796
1797
1798
	/**
1799
	 * Add toolbar button
1800
	 * @param string $href
1801
	 * @param string $text
1802
	 * @param array  $params
1803
	 * @return ToolbarButton
1804
	 */
1805
	public function addToolbarButton($href, $text = '', $params = [])
1806
	{
1807
		$button = new ToolbarButton($this, $href, $text, $params);
1808
1809
		return $this->toolbar_buttons[] = $button;
1810
	}
1811
1812
1813
	/********************************************************************************
1814
	 *                                 GROUP ACTIONS                                *
1815
	 ********************************************************************************/
1816
1817
1818
	/**
1819
	 * Alias for add group select action
1820
	 * @param string $title
1821
	 * @param array  $options
1822
	 * @return GroupAction\GroupAction
1823
	 */
1824
	public function addGroupAction($title, $options = [])
1825
	{
1826
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1827
	}
1828
1829
1830
	/**
1831
	 * Add group action (select box)
1832
	 * @param string $title
1833
	 * @param array  $options
1834
	 * @return GroupAction\GroupAction
1835
	 */
1836
	public function addGroupSelectAction($title, $options = [])
1837
	{
1838
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1839
	}
1840
1841
1842
	/**
1843
	 * Add group action (text input)
1844
	 * @param string $title
1845
	 * @return GroupAction\GroupAction
1846
	 */
1847
	public function addGroupTextAction($title)
1848
	{
1849
		return $this->getGroupActionCollection()->addGroupTextAction($title);
1850
	}
1851
1852
1853
	/**
1854
	 * Add group action (textarea)
1855
	 * @param string $title
1856
	 * @return GroupAction\GroupAction
1857
	 */
1858
	public function addGroupTextareaAction($title)
1859
	{
1860
		return $this->getGroupActionCollection()->addGroupTextareaAction($title);
1861
	}
1862
1863
1864
	/**
1865
	 * Get collection of all group actions
1866
	 * @return GroupAction\GroupActionCollection
1867
	 */
1868
	public function getGroupActionCollection()
1869
	{
1870
		if (!$this->group_action_collection) {
1871
			$this->group_action_collection = new GroupAction\GroupActionCollection($this);
1872
		}
1873
1874
		return $this->group_action_collection;
1875
	}
1876
1877
1878
	/**
1879
	 * Has datagrid some group actions?
1880
	 * @return boolean
1881
	 */
1882
	public function hasGroupActions()
1883
	{
1884
		return (bool) $this->group_action_collection;
1885
	}
1886
1887
1888
	/********************************************************************************
1889
	 *                                   HANDLERS                                   *
1890
	 ********************************************************************************/
1891
1892
1893
	/**
1894
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1895
	 * @param  int  $page
1896
	 * @return void
1897
	 */
1898
	public function handlePage($page)
1899
	{
1900
		/**
1901
		 * Session stuff
1902
		 */
1903
		$this->page = $page;
1904
		$this->saveSessionData('_grid_page', $page);
1905
1906
		$this->reload(['table']);
1907
	}
1908
1909
1910
	/**
1911
	 * Handler for sorting
1912
	 * @param array $sort
1913
	 * @return void
1914
	 */
1915
	public function handleSort(array $sort)
1916
	{
1917
		$new_sort = [];
1918
1919
		/**
1920
		 * Find apropirate column
1921
		 */
1922
		foreach ($sort as $key => $value) {
1923
			if (empty($this->columns[$key])) {
1924
				throw new DataGridException("Column <$key> not found");
1925
			}
1926
1927
			$column = $this->columns[$key];
1928
			$new_sort = [$key => $value];
1929
1930
			/**
1931
			 * Pagination may be reseted after sorting
1932
			 */
1933
			if ($column->sortableResetPagination()) {
1934
				$this->page = 1;
1935
				$this->saveSessionData('_grid_page', 1);
1936
			}
1937
1938
			/**
1939
			 * Custom sorting callback may be applied
1940
			 */
1941
			if ($column->getSortableCallback()) {
1942
				$this->sort_callback = $column->getSortableCallback();
1943
			}
1944
		}
1945
1946
		/**
1947
		 * Session stuff
1948
		 */
1949
		$this->sort = $new_sort;
1950
		$this->saveSessionData('_grid_sort', $this->sort);
1951
1952
		$this->reload(['table']);
1953
	}
1954
1955
1956
	/**
1957
	 * handler for reseting the filter
1958
	 * @return void
1959
	 */
1960
	public function handleResetFilter()
1961
	{
1962
		/**
1963
		 * Session stuff
1964
		 */
1965
		$this->deleteSesssionData('_grid_page');
1966
1967
		if ($this->default_filter_use_on_reset) {
1968
			$this->deleteSesssionData('_grid_has_filtered');
1969
		}
1970
1971
		foreach ($this->getSessionData() as $key => $value) {
1972
			if (!in_array($key, [
1973
				'_grid_per_page',
1974
				'_grid_sort',
1975
				'_grid_page',
1976
				'_grid_has_filtered',
1977
				'_grid_hidden_columns',
1978
				'_grid_hidden_columns_manipulated'
1979
				])) {
1980
1981
				$this->deleteSesssionData($key);
1982
			}
1983
		}
1984
1985
		$this->filter = [];
1986
1987
		$this->reload(['grid']);
1988
	}
1989
1990
1991
	/**
1992
	 * Handler for export
1993
	 * @param  int $id Key for particular export class in array $this->exports
1994
	 * @return void
1995
	 */
1996
	public function handleExport($id)
1997
	{
1998
		if (!isset($this->exports[$id])) {
1999
			throw new Nette\Application\ForbiddenRequestException;
2000
		}
2001
2002
		if (!empty($this->columns_export_order)) {
2003
			$this->setColumnsOrder($this->columns_export_order);
2004
		}
2005
2006
		$export = $this->exports[$id];
2007
2008
		/**
2009
		 * Invoke possible events
2010
		 */
2011
		$this->onExport($this);
0 ignored issues
show
Documentation Bug introduced by
The method onExport does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
2012
2013
		if ($export->isFiltered()) {
2014
			$sort      = $this->sort;
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2015
			$filter    = $this->assableFilters();
2016
		} else {
2017
			$sort      = [$this->primary_key => 'ASC'];
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2018
			$filter    = [];
2019
		}
2020
2021
		if (NULL === $this->dataModel) {
2022
			throw new DataGridException('You have to set a data source first.');
2023
		}
2024
2025
		$rows = [];
2026
2027
		$items = Nette\Utils\Callback::invokeArgs(
2028
			[$this->dataModel, 'filterData'], [
2029
				NULL,
2030
				$this->createSorting($this->sort, $this->sort_callback),
2031
				$filter
2032
			]
2033
		);
2034
2035
		foreach ($items as $item) {
2036
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
2037
		}
2038
2039
		if ($export instanceof Export\ExportCsv) {
2040
			$export->invoke($rows);
2041
		} else {
2042
			$export->invoke($items);
2043
		}
2044
2045
		if ($export->isAjax()) {
2046
			$this->reload();
2047
		}
2048
	}
2049
2050
2051
	/**
2052
	 * Handler for getting children of parent item (e.g. category)
2053
	 * @param  int $parent
2054
	 * @return void
2055
	 */
2056
	public function handleGetChildren($parent)
2057
	{
2058
		$this->setDataSource(
2059
			call_user_func($this->tree_view_children_callback, $parent)
2060
		);
2061
2062
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2063
			$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2064
			$this->getPresenter()->payload->_datagrid_tree = $parent;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2065
2066
			$this->redrawControl('items');
2067
2068
			$this->onRedraw();
2069
		} else {
2070
			$this->getPresenter()->redirect('this');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method redirect() does only exist in the following implementations of said interface: Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Ublaboo\DataGrid\Compone...nator\DataGridPaginator, Ublaboo\DataGrid\DataGrid.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2071
		}
2072
	}
2073
2074
2075
	/**
2076
	 * Handler for getting item detail
2077
	 * @param  mixed $id
2078
	 * @return void
2079
	 */
2080
	public function handleGetItemDetail($id)
2081
	{
2082
		$this->getTemplate()->add('toggle_detail', $id);
2083
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
2084
2085
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2086
			$this->getPresenter()->payload->_datagrid_toggle_detail = $id;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2087
			$this->redrawControl('items');
2088
2089
			$this->onRedraw();
2090
		} else {
2091
			$this->getPresenter()->redirect('this');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method redirect() does only exist in the following implementations of said interface: Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Ublaboo\DataGrid\Compone...nator\DataGridPaginator, Ublaboo\DataGrid\DataGrid.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2092
		}
2093
	}
2094
2095
2096
	/**
2097
	 * Handler for inline editing
2098
	 * @param  mixed $id
2099
	 * @param  mixed $key
2100
	 * @return void
2101
	 */
2102
	public function handleEdit($id, $key)
2103
	{
2104
		$column = $this->getColumn($key);
2105
		$value = $this->getPresenter()->getRequest()->getPost('value');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getRequest() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2106
2107
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
2108
	}
2109
2110
2111
	/**
2112
	 * Redraw $this
2113
	 * @return void
2114
	 */
2115
	public function reload($snippets = [])
2116
	{
2117
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2118
			$this->redrawControl('tbody');
2119
			$this->redrawControl('pagination');
2120
2121
			/**
2122
			 * manualy reset exports links...
2123
			 */
2124
			$this->resetExportsLinks();
2125
			$this->redrawControl('exports');
2126
2127
			foreach ($snippets as $snippet) {
2128
				$this->redrawControl($snippet);
2129
			}
2130
2131
			$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2132
2133
			$this->onRedraw();
2134
		} else {
2135
			$this->getPresenter()->redirect('this');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method redirect() does only exist in the following implementations of said interface: Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Ublaboo\DataGrid\Compone...nator\DataGridPaginator, Ublaboo\DataGrid\DataGrid.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2136
		}
2137
	}
2138
2139
2140
	/**
2141
	 * Handler for column status
2142
	 * @param  string $id
2143
	 * @param  string $key
2144
	 * @param  string $value
2145
	 * @return void
2146
	 */
2147
	public function handleChangeStatus($id, $key, $value)
2148
	{
2149
		if (empty($this->columns[$key])) {
2150
			throw new DataGridException("ColumnStatus[$key] does not exist");
2151
		}
2152
2153
		$this->columns[$key]->onChange($id, $value);
2154
	}
2155
2156
2157
	/**
2158
	 * Redraw just one row via ajax
2159
	 * @param  int   $id
2160
	 * @param  mixed $primary_where_column
2161
	 * @return void
2162
	 */
2163
	public function redrawItem($id, $primary_where_column = NULL)
2164
	{
2165
		$this->snippets_set = TRUE;
2166
2167
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
2168
2169
		$this->redrawControl('items');
2170
2171
		$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2172
2173
		$this->onRedraw();
2174
	}
2175
2176
2177
	/**
2178
	 * Tell datagrid to display all columns
2179
	 * @return void
2180
	 */
2181
	public function handleShowAllColumns()
2182
	{
2183
		$this->deleteSesssionData('_grid_hidden_columns');
2184
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2185
2186
		$this->redrawControl();
2187
2188
		$this->onRedraw();
2189
	}
2190
2191
2192
	/**
2193
	 * Tell datagrid to display default columns
2194
	 * @return void
2195
	 */
2196
	public function handleShowDefaultColumns()
2197
	{
2198
		$this->deleteSesssionData('_grid_hidden_columns');
2199
		$this->saveSessionData('_grid_hidden_columns_manipulated', FALSE);
2200
2201
		$this->redrawControl();
2202
2203
		$this->onRedraw();
2204
	}
2205
2206
2207
	/**
2208
	 * Reveal particular column
2209
	 * @param  string $column
2210
	 * @return void
2211
	 */
2212 View Code Duplication
	public function handleShowColumn($column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2213
	{
2214
		$columns = $this->getSessionData('_grid_hidden_columns');
2215
2216
		if (!empty($columns)) {
2217
			$pos = array_search($column, $columns);
2218
2219
			if ($pos !== FALSE) {
2220
				unset($columns[$pos]);
2221
			}
2222
		}
2223
2224
		$this->saveSessionData('_grid_hidden_columns', $columns);
2225
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2226
2227
		$this->redrawControl();
2228
2229
		$this->onRedraw();
2230
	}
2231
2232
2233
	/**
2234
	 * Notice datagrid to not display particular columns
2235
	 * @param  string $column
2236
	 * @return void
2237
	 */
2238 View Code Duplication
	public function handleHideColumn($column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2239
	{
2240
		/**
2241
		 * Store info about hiding a column to session
2242
		 */
2243
		$columns = $this->getSessionData('_grid_hidden_columns');
2244
2245
		if (empty($columns)) {
2246
			$columns = [$column];
2247
		} else if (!in_array($column, $columns)) {
2248
			array_push($columns, $column);
2249
		}
2250
2251
		$this->saveSessionData('_grid_hidden_columns', $columns);
2252
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2253
2254
		$this->redrawControl();
2255
2256
		$this->onRedraw();
2257
	}
2258
2259
2260
	public function handleActionCallback($__key, $__id)
2261
	{
2262
		$action = $this->getAction($__key);
2263
2264
		if (!($action instanceof Column\ActionCallback)) {
2265
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
2266
		}
2267
2268
		$action->onClick($__id);
2269
	}
2270
2271
2272
	/********************************************************************************
2273
	 *                                  PAGINATION                                  *
2274
	 ********************************************************************************/
2275
2276
2277
	/**
2278
	 * Set options of select "items_per_page"
2279
	 * @param array $items_per_page_list
2280
	 * @return static
2281
	 */
2282
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
2283
	{
2284
		$this->items_per_page_list = $items_per_page_list;
2285
2286
		if ($include_all) {
2287
			$this->items_per_page_list[] = 'all';
2288
		}
2289
2290
		return $this;
2291
	}
2292
2293
2294
	/**
2295
	 * Set default "items per page" value in pagination select
2296
	 * @param $count
2297
	 * @return static
2298
	 */
2299
	public function setDefaultPerPage($count)
2300
	{
2301
		$this->default_per_page = $count;
2302
2303
		return $this;
2304
	}
2305
2306
2307
	/**
2308
	 * User may set default "items per page" value, apply it
2309
	 * @return void
2310
	 */
2311
	public function findDefaultPerPage()
2312
	{
2313
		if (!empty($this->per_page)) {
2314
			return;
2315
		}
2316
2317
		if (!empty($this->default_per_page)) {
2318
			$this->per_page = $this->default_per_page;
2319
		}
2320
2321
		$this->saveSessionData('_grid_per_page', $this->per_page);
2322
	}
2323
2324
2325
	/**
2326
	 * Paginator factory
2327
	 * @return Components\DataGridPaginator\DataGridPaginator
2328
	 */
2329
	public function createComponentPaginator()
2330
	{
2331
		/**
2332
		 * Init paginator
2333
		 */
2334
		$component = new Components\DataGridPaginator\DataGridPaginator(
2335
			$this->getTranslator(),
2336
			static::$icon_prefix
2337
		);
2338
		$paginator = $component->getPaginator();
2339
2340
		$paginator->setPage($this->page);
2341
		$paginator->setItemsPerPage($this->getPerPage());
2342
2343
		return $component;
2344
	}
2345
2346
2347
	/**
2348
	 * Get parameter per_page
2349
	 * @return int
2350
	 */
2351
	public function getPerPage()
2352
	{
2353
		$items_per_page_list = $this->getItemsPerPageList();
2354
2355
		$per_page = $this->per_page ?: reset($items_per_page_list);
2356
2357
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
2358
			$per_page = reset($items_per_page_list);
2359
		}
2360
2361
		return $per_page;
2362
	}
2363
2364
2365
	/**
2366
	 * Get associative array of items_per_page_list
2367
	 * @return array
2368
	 */
2369
	public function getItemsPerPageList()
2370
	{
2371
		if (empty($this->items_per_page_list)) {
2372
			$this->setItemsPerPageList([10, 20, 50], TRUE);
2373
		}
2374
2375
		$list = array_flip($this->items_per_page_list);
2376
2377
		foreach ($list as $key => $value) {
2378
			$list[$key] = $key;
2379
		}
2380
2381
		if (array_key_exists('all', $list)) {
2382
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
2383
		}
2384
2385
		return $list;
2386
	}
2387
2388
2389
	/**
2390
	 * Order Grid to "be paginated"
2391
	 * @param bool $do
2392
	 * @return static
2393
	 */
2394
	public function setPagination($do)
2395
	{
2396
		$this->do_paginate = (bool) $do;
2397
2398
		return $this;
2399
	}
2400
2401
2402
	/**
2403
	 * Tell whether Grid is paginated
2404
	 * @return bool
2405
	 */
2406
	public function isPaginated()
2407
	{
2408
		return $this->do_paginate;
2409
	}
2410
2411
2412
	/**
2413
	 * Return current paginator class
2414
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
2415
	 */
2416
	public function getPaginator()
2417
	{
2418
		if ($this->isPaginated() && $this->getPerPage() !== 'all') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $this->getPerPage() (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
2419
			return $this['paginator'];
2420
		}
2421
2422
		return NULL;
2423
	}
2424
2425
2426
	/********************************************************************************
2427
	 *                                     I18N                                     *
2428
	 ********************************************************************************/
2429
2430
2431
	/**
2432
	 * Set datagrid translator
2433
	 * @param Nette\Localization\ITranslator $translator
2434
	 * @return static
2435
	 */
2436
	public function setTranslator(Nette\Localization\ITranslator $translator)
2437
	{
2438
		$this->translator = $translator;
2439
2440
		return $this;
2441
	}
2442
2443
2444
	/**
2445
	 * Get translator for datagrid
2446
	 * @return Nette\Localization\ITranslator
2447
	 */
2448
	public function getTranslator()
2449
	{
2450
		if (!$this->translator) {
2451
			$this->translator = new Localization\SimpleTranslator;
2452
		}
2453
2454
		return $this->translator;
2455
	}
2456
2457
2458
	/********************************************************************************
2459
	 *                                 COLUMNS ORDER                                *
2460
	 ********************************************************************************/
2461
2462
2463
	/**
2464
	 * Set order of datagrid columns
2465
	 * @param array $order
2466
	 * @return static
2467
	 */
2468
	public function setColumnsOrder($order)
2469
	{
2470
		$new_order = [];
2471
2472
		foreach ($order as $key) {
2473
			if (isset($this->columns[$key])) {
2474
				$new_order[$key] = $this->columns[$key];
2475
			}
2476
		}
2477
2478
		if (sizeof($new_order) === sizeof($this->columns)) {
2479
			$this->columns = $new_order;
2480
		} else {
2481
			throw new DataGridException('When changing columns order, you have to specify all columns');
2482
		}
2483
2484
		return $this;
2485
	}
2486
2487
2488
	/**
2489
	 * Columns order may be different for export and normal grid
2490
	 * @param array $order
2491
	 */
2492
	public function setColumnsExportOrder($order)
2493
	{
2494
		$this->columns_export_order = (array) $order;
2495
	}
2496
2497
2498
	/********************************************************************************
2499
	 *                                SESSION & URL                                 *
2500
	 ********************************************************************************/
2501
2502
2503
	/**
2504
	 * Find some unique session key name
2505
	 * @return string
2506
	 */
2507
	public function getSessionSectionName()
2508
	{
2509
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
2510
	}
2511
2512
2513
	/**
2514
	 * Should datagrid remember its filters/pagination/etc using session?
2515
	 * @param bool $remember
2516
	 * @return static
2517
	 */
2518
	public function setRememberState($remember = TRUE)
2519
	{
2520
		$this->remember_state = (bool) $remember;
2521
2522
		return $this;
2523
	}
2524
2525
2526
	/**
2527
	 * Should datagrid refresh url using history API?
2528
	 * @param bool $refresh
2529
	 * @return static
2530
	 */
2531
	public function setRefreshUrl($refresh = TRUE)
2532
	{
2533
		$this->refresh_url = (bool) $refresh;
2534
2535
2536
		return $this;
2537
	}
2538
2539
2540
	/**
2541
	 * Get session data if functionality is enabled
2542
	 * @param  string $key
2543
	 * @return mixed
2544
	 */
2545
	public function getSessionData($key = NULL, $default_value = NULL)
2546
	{
2547
		if (!$this->remember_state) {
2548
			return $key ? $default_value : [];
2549
		}
2550
2551
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2552
	}
2553
2554
2555
	/**
2556
	 * Save session data - just if it is enabled
2557
	 * @param  string $key
2558
	 * @param  mixed  $value
2559
	 * @return void
2560
	 */
2561
	public function saveSessionData($key, $value)
2562
	{
2563
		if ($this->remember_state) {
2564
			$this->grid_session->{$key} = $value;
2565
		}
2566
	}
2567
2568
2569
	/**
2570
	 * Delete session data
2571
	 * @return void
2572
	 */
2573
	public function deleteSesssionData($key)
2574
	{
2575
		unset($this->grid_session->{$key});
2576
	}
2577
2578
2579
	/********************************************************************************
2580
	 *                                  ITEM DETAIL                                 *
2581
	 ********************************************************************************/
2582
2583
2584
	/**
2585
	 * Get items detail parameters
2586
	 * @return array
2587
	 */
2588
	public function getItemsDetail()
2589
	{
2590
		return $this->items_detail;
2591
	}
2592
2593
2594
	/**
2595
	 * Items can have thair detail - toggled
2596
	 * @param mixed $detail callable|string|bool
2597
	 * @param bool|NULL $primary_where_column
2598
	 * @return Column\ItemDetail
2599
	 */
2600
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2601
	{
2602
		if ($this->isSortable()) {
2603
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2604
		}
2605
2606
		$this->items_detail = new Column\ItemDetail(
2607
			$this,
2608
			$primary_where_column ?: $this->primary_key
0 ignored issues
show
Bug introduced by
It seems like $primary_where_column ?: $this->primary_key can also be of type boolean; however, Ublaboo\DataGrid\Column\ItemDetail::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2609
		);
2610
2611
		if (is_string($detail)) {
2612
			/**
2613
			 * Item detail will be in separate template
2614
			 */
2615
			$this->items_detail->setType('template');
2616
			$this->items_detail->setTemplate($detail);
2617
2618
		} else if (is_callable($detail)) {
2619
			/**
2620
			 * Item detail will be rendered via custom callback renderer
2621
			 */
2622
			$this->items_detail->setType('renderer');
2623
			$this->items_detail->setRenderer($detail);
2624
2625
		} else if (TRUE === $detail) {
2626
			/**
2627
			 * Item detail will be rendered probably via block #detail
2628
			 */
2629
			$this->items_detail->setType('block');
2630
2631
		} else {
2632
			throw new DataGridException(
2633
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2634
			);
2635
		}
2636
2637
		return $this->items_detail;
2638
	}
2639
2640
2641
	/**
2642
	 * @param callable $callable_set_container 
2643
	 * @return static
2644
	 */
2645
	public function setItemsDetailForm(callable $callable_set_container)
2646
	{
2647
		if ($this->items_detail instanceof Column\ItemDetail) {
2648
			$this->items_detail->setForm(
2649
				new Utils\ItemDetailForm($callable_set_container)
2650
			);
2651
2652
			return $this;
2653
		}
2654
2655
		throw new DataGridException('Please set the ItemDetail first.');
2656
	}
2657
2658
2659
	/**
2660
	 * @return Nette\Forms\Container|NULL
2661
	 */
2662
	public function getItemDetailForm()
2663
	{
2664
		if ($this->items_detail instanceof Column\ItemDetail) {
2665
			return $this->items_detail->getForm();
2666
		}
2667
2668
		return NULL;
2669
	}
2670
2671
2672
	/********************************************************************************
2673
	 *                                ROW PRIVILEGES                                *
2674
	 ********************************************************************************/
2675
2676
2677
	/**
2678
	 * @param  callable $condition
2679
	 * @return void
2680
	 */
2681
	public function allowRowsGroupAction(callable $condition)
2682
	{
2683
		$this->row_conditions['group_action'] = $condition;
2684
	}
2685
2686
2687
	/**
2688
	 * @param  callable $condition
2689
	 * @return void
2690
	 */
2691
	public function allowRowsInlineEdit(callable $condition)
2692
	{
2693
		$this->row_conditions['inline_edit'] = $condition;
2694
	}
2695
2696
2697
	/**
2698
	 * @param  string   $key
2699
	 * @param  callable $condition
2700
	 * @return void
2701
	 */
2702
	public function allowRowsAction($key, callable $condition)
2703
	{
2704
		$this->row_conditions['action'][$key] = $condition;
2705
	}
2706
2707
2708
	/**
2709
	 * @param  string      $name
2710
	 * @param  string|null $key
2711
	 * @return bool|callable
2712
	 */
2713
	public function getRowCondition($name, $key = NULL)
2714
	{
2715
		if (!isset($this->row_conditions[$name])) {
2716
			return FALSE;
2717
		}
2718
2719
		$condition = $this->row_conditions[$name];
2720
2721
		if (!$key) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $key of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2722
			return $condition;
2723
		}
2724
2725
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2726
	}
2727
2728
2729
	/********************************************************************************
2730
	 *                               COLUMN CALLBACK                                *
2731
	 ********************************************************************************/
2732
2733
2734
	/**
2735
	 * @param  string   $key
2736
	 * @param  callable $callback
2737
	 * @return void
2738
	 */
2739
	public function addColumnCallback($key, callable $callback)
2740
	{
2741
		$this->column_callbacks[$key] = $callback;
2742
	}
2743
2744
2745
	/**
2746
	 * @param  string $key
2747
	 * @return callable|null
2748
	 */
2749
	public function getColumnCallback($key)
2750
	{
2751
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
2752
	}
2753
2754
2755
	/********************************************************************************
2756
	 *                                 INLINE EDIT                                  *
2757
	 ********************************************************************************/
2758
2759
2760
	/**
2761
	 * @return InlineEdit
2762
	 */
2763
	public function addInlineEdit($primary_where_column = NULL)
2764
	{
2765
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2766
2767
		return $this->inlineEdit;
2768
	}
2769
2770
2771
	/**
2772
	 * @return InlineEdit|null
2773
	 */
2774
	public function getInlineEdit()
2775
	{
2776
		return $this->inlineEdit;
2777
	}
2778
2779
2780
	/**
2781
	 * @param  mixed $id
2782
	 * @return void
2783
	 */
2784
	public function handleInlineEdit($id)
2785
	{
2786
		if ($this->inlineEdit) {
2787
			$this->inlineEdit->setItemId($id);
2788
2789
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2790
2791
			$this['filter']['inline_edit']->addHidden('_id', $id);
2792
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2793
2794
			if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
2795
				$this->getPresenter()->payload->_datagrid_inline_editing = TRUE;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2796
			}
2797
2798
			$this->redrawItem($id, $primary_where_column);
2799
		}
2800
	}
2801
2802
2803
	/********************************************************************************
2804
	 *                                  INLINE ADD                                  *
2805
	 ********************************************************************************/
2806
2807
2808
	/**
2809
	 * @return InlineEdit
2810
	 */
2811
	public function addInlineAdd()
2812
	{
2813
		$this->inlineAdd = new InlineEdit($this);
2814
2815
		$this->inlineAdd
2816
			->setIcon('plus')
2817
			->setClass('btn btn-xs btn-default');
2818
2819
		return $this->inlineAdd;
2820
	}
2821
2822
2823
	/**
2824
	 * @return InlineEdit|null
2825
	 */
2826
	public function getInlineAdd()
2827
	{
2828
		return $this->inlineAdd;
2829
	}
2830
2831
2832
	/********************************************************************************
2833
	 *                               HIDEABLE COLUMNS                               *
2834
	 ********************************************************************************/
2835
2836
2837
	/**
2838
	 * Can datagrid hide colums?
2839
	 * @return boolean
2840
	 */
2841
	public function canHideColumns()
2842
	{
2843
		return (bool) $this->can_hide_columns;
2844
	}
2845
2846
2847
	/**
2848
	 * Order Grid to set columns hideable.
2849
	 * @return static
2850
	 */
2851
	public function setColumnsHideable()
2852
	{
2853
		$this->can_hide_columns = TRUE;
2854
2855
		return $this;
2856
	}
2857
2858
2859
	/********************************************************************************
2860
	 *                                COLUMNS SUMMARY                               *
2861
	 ********************************************************************************/
2862
2863
2864
	/**
2865
	 * Will datagrid show summary in the end?
2866
	 * @return bool
2867
	 */
2868
	public function hasColumnsSummary()
2869
	{
2870
		return $this->columnsSummary instanceof ColumnsSummary;
2871
	}
2872
2873
2874
	/**
2875
	 * Set columns to be summarized in the end.
2876
	 * @param array    $columns
2877
	 * @param callable $rowCallback
2878
	 * @return \Ublaboo\DataGrid\ColumnsSummary
2879
	 */
2880
	public function setColumnsSummary(array $columns, $rowCallback = NULL)
2881
	{
2882
		if (!empty($rowCallback)) {
2883
			if (!is_callable($rowCallback)) {
2884
				throw new \InvalidArgumentException('Row summary callback must be callable');
2885
			}
2886
		}
2887
2888
		$this->columnsSummary = new ColumnsSummary($this, $columns, $rowCallback);
2889
2890
		return $this->columnsSummary;
2891
	}
2892
2893
2894
	/**
2895
	 * @return ColumnsSummary|NULL
2896
	 */
2897
	public function getColumnsSummary()
2898
	{
2899
		return $this->columnsSummary;
2900
	}
2901
2902
2903
	/********************************************************************************
2904
	 *                                   INTERNAL                                   *
2905
	 ********************************************************************************/
2906
2907
2908
	/**
2909
	 * Tell grid filters to by submitted automatically
2910
	 * @param bool $auto
2911
	 */
2912
	public function setAutoSubmit($auto = TRUE)
2913
	{
2914
		$this->auto_submit = (bool) $auto;
2915
2916
		return $this;
2917
	}
2918
2919
2920
	/**
2921
	 * @return bool
2922
	 */
2923
	public function hasAutoSubmit()
2924
	{
2925
		return $this->auto_submit;
2926
	}
2927
2928
2929
	/**
2930
	 * Submit button when no auto-submitting is used
2931
	 * @return Filter\SubmitButton
2932
	 */
2933
	public function getFilterSubmitButton()
2934
	{
2935
		if ($this->hasAutoSubmit()) {
2936
			throw new DataGridException(
2937
				'DataGrid has auto-submit. Turn it off before setting filter submit button.'
2938
			);
2939
		}
2940
2941
		if ($this->filter_submit_button === NULL) {
2942
			$this->filter_submit_button = new Filter\SubmitButton($this);
2943
		}
2944
2945
		return $this->filter_submit_button;
2946
	}
2947
2948
2949
	/********************************************************************************
2950
	 *                                   INTERNAL                                   *
2951
	 ********************************************************************************/
2952
2953
2954
	/**
2955
	 * Get count of columns
2956
	 * @return int
2957
	 */
2958
	public function getColumnsCount()
2959
	{
2960
		$count = sizeof($this->getColumns());
2961
2962
		if (!empty($this->actions)
2963
			|| $this->isSortable()
2964
			|| $this->getItemsDetail()
2965
			|| $this->getInlineEdit()
2966
			|| $this->getInlineAdd()) {
2967
			$count++;
2968
		}
2969
2970
		if ($this->hasGroupActions()) {
2971
			$count++;
2972
		}
2973
2974
		return $count;
2975
	}
2976
2977
2978
	/**
2979
	 * Get primary key of datagrid data source
2980
	 * @return string
2981
	 */
2982
	public function getPrimaryKey()
2983
	{
2984
		return $this->primary_key;
2985
	}
2986
2987
2988
	/**
2989
	 * Get set of set columns
2990
	 * @return Column\IColumn[]
2991
	 */
2992
	public function getColumns()
2993
	{
2994
		$return = $this->columns;
2995
2996
		if (!$this->getSessionData('_grid_hidden_columns_manipulated', FALSE)) {
2997
			$columns_to_hide = [];
2998
2999
			foreach ($this->columns as $key => $column) {
3000
				if ($column->getDefaultHide()) {
3001
					$columns_to_hide[] = $key;
3002
				}
3003
			}
3004
3005
			if (!empty($columns_to_hide)) {
3006
				$this->saveSessionData('_grid_hidden_columns', $columns_to_hide);
3007
				$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
3008
			}
3009
		}
3010
3011
		$hidden_columns = $this->getSessionData('_grid_hidden_columns', []);
3012
3013
		foreach ($hidden_columns as $column) {
3014
			if (!empty($this->columns[$column])) {
3015
				$this->columns_visibility[$column] = [
3016
					'visible' => FALSE
3017
				];
3018
3019
				unset($return[$column]);
3020
			}
3021
		}
3022
3023
		return $return;
3024
	}
3025
3026
3027
	public function getColumnsVisibility()
3028
	{
3029
		$return = $this->columns_visibility;
3030
3031
		foreach ($this->columns_visibility as $key => $column) {
3032
			$return[$key]['column'] = $this->columns[$key];
3033
		}
3034
3035
		return $return;
3036
	}
3037
3038
3039
	/**
3040
	 * @return PresenterComponent
3041
	 */
3042
	public function getParent()
3043
	{
3044
		$parent = parent::getParent();
3045
3046
		if (!($parent instanceof PresenterComponent)) {
0 ignored issues
show
Bug introduced by
The class Nette\Application\UI\PresenterComponent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3047
			throw new DataGridHasToBeAttachedToPresenterComponentException(
3048
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
3049
			);
3050
		}
3051
3052
		return $parent;
3053
	}
3054
3055
3056
	/**
3057
	 * Some of datagrid columns is hidden by default
3058
	 * @param bool $default_hide
3059
	 */
3060
	public function setSomeColumnDefaultHide($default_hide)
3061
	{
3062
		$this->some_column_default_hide = $default_hide;
3063
	}
3064
3065
3066
	/**
3067
	 * Are some of columns hidden bydefault?
3068
	 */
3069
	public function hasSomeColumnDefaultHide()
3070
	{
3071
		return $this->some_column_default_hide;
3072
	}
3073
3074
}
3075