Completed
Pull Request — master (#267)
by
unknown
03:32
created

DataGrid::handleShowColumn()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 19
Ratio 100 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 19
loc 19
rs 9.4285
cc 3
eloc 10
nc 3
nop 1
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2015 ublaboo <[email protected]>
5
 * @author      Pavel Janda <[email protected]>
6
 * @package     Ublaboo
7
 */
8
9
namespace Ublaboo\DataGrid;
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 $onColumnAdd;
45
46
	/**
47
	 * @var string
48
	 */
49
	public static $icon_prefix = 'fa fa-';
50
51
	/**
52
	 * Default form method
53
	 * @var string
54
	 */
55
	public static $form_method = 'post';
56
57
	/**
58
	 * When set to TRUE, datagrid throws an exception
59
	 * 	when tring to get related entity within join and entity does not exist
60
	 * @var bool
61
	 */
62
	public $strict_entity_property = FALSE;
63
64
	/**
65
	 * @var int
66
	 * @persistent
67
	 */
68
	public $page = 1;
69
70
	/**
71
	 * @var int|string
72
	 * @persistent
73
	 */
74
	public $per_page;
75
76
	/**
77
	 * @var array
78
	 * @persistent
79
	 */
80
	public $sort = [];
81
82
	/**
83
	 * @var array
84
	 */
85
	public $default_sort = [];
86
87
	/**
88
	 * @var array
89
	 */
90
	public $default_filter = [];
91
92
	/**
93
	 * @var bool
94
	 */
95
	public $default_filter_use_on_reset = TRUE;
96
97
	/**
98
	 * @var array
99
	 * @persistent
100
	 */
101
	public $filter = [];
102
103
	/**
104
	 * @var callable|null
105
	 */
106
	protected $sort_callback = NULL;
107
108
	/**
109
	 * @var bool
110
	 */
111
	protected $use_happy_components = TRUE;
112
113
	/**
114
	 * @var callable
115
	 */
116
	protected $rowCallback;
117
118
	/**
119
	 * @var array
120
	 */
121
	protected $items_per_page_list;
122
123
	/**
124
	 * @var int
125
	 */
126
	protected $default_per_page;
127
128
	/**
129
	 * @var string
130
	 */
131
	protected $template_file;
132
133
	/**
134
	 * @var Column\IColumn[]
135
	 */
136
	protected $columns = [];
137
138
	/**
139
	 * @var Column\Action[]
140
	 */
141
	protected $actions = [];
142
143
	/**
144
	 * @var GroupAction\GroupActionCollection
145
	 */
146
	protected $group_action_collection;
147
148
	/**
149
	 * @var Filter\Filter[]
150
	 */
151
	protected $filters = [];
152
153
	/**
154
	 * @var Export\Export[]
155
	 */
156
	protected $exports = [];
157
158
	/**
159
	 * @var ToolbarButton[]
160
	 */
161
	protected $toolbar_buttons = [];
162
163
	/**
164
	 * @var DataModel
165
	 */
166
	protected $dataModel;
167
168
	/**
169
	 * @var DataFilter
170
	 */
171
	protected $dataFilter;
172
173
	/**
174
	 * @var string
175
	 */
176
	protected $primary_key = 'id';
177
178
	/**
179
	 * @var bool
180
	 */
181
	protected $do_paginate = TRUE;
182
183
	/**
184
	 * @var bool
185
	 */
186
	protected $csv_export = TRUE;
187
188
	/**
189
	 * @var bool
190
	 */
191
	protected $csv_export_filtered = TRUE;
192
193
	/**
194
	 * @var bool
195
	 */
196
	protected $sortable = FALSE;
197
198
	/**
199
	 * @var string
200
	 */
201
	protected $sortable_handler = 'sort!';
202
203
	/**
204
	 * @var string
205
	 */
206
	protected $original_template;
207
208
	/**
209
	 * @var array
210
	 */
211
	protected $redraw_item;
212
213
	/**
214
	 * @var mixed
215
	 */
216
	protected $translator;
217
218
	/**
219
	 * @var bool
220
	 */
221
	protected $force_filter_active;
222
223
	/**
224
	 * @var callable
225
	 */
226
	protected $tree_view_children_callback;
227
228
	/**
229
	 * @var callable
230
	 */
231
	protected $tree_view_has_children_callback;
232
233
	/**
234
	 * @var string
235
	 */
236
	protected $tree_view_has_children_column;
237
238
	/**
239
	 * @var bool
240
	 */
241
	protected $outer_filter_rendering = FALSE;
242
243
	/**
244
	 * @var array
245
	 */
246
	protected $columns_export_order = [];
247
248
	/**
249
	 * @var bool
250
	 */
251
	protected $remember_state = TRUE;
252
253
	/**
254
	 * @var bool
255
	 */
256
	protected $refresh_url = TRUE;
257
258
	/**
259
	 * @var Nette\Http\SessionSection
260
	 */
261
	protected $grid_session;
262
263
	/**
264
	 * @var Column\ItemDetail
265
	 */
266
	protected $items_detail;
267
268
	/**
269
	 * @var array
270
	 */
271
	protected $row_conditions = [
272
		'group_action' => FALSE,
273
		'action' => []
274
	];
275
276
	/**
277
	 * @var array
278
	 */
279
	protected $column_callbacks = [];
280
281
	/**
282
	 * @var bool
283
	 */
284
	protected $can_hide_columns = FALSE;
285
286
	/**
287
	 * @var array
288
	 */
289
	protected $columns_visibility = [];
290
291
	/**
292
	 * @var InlineEdit
293
	 */
294
	protected $inlineEdit;
295
296
	/**
297
	 * @var InlineEdit
298
	 */
299
	protected $inlineAdd;
300
301
	/**
302
	 * @var bool
303
	 */
304
	protected $snippets_set = FALSE;
305
306
	/**
307
	 * @var bool
308
	 */
309
	protected $some_column_default_hide = FALSE;
310
311
	/**
312
	 * @var ColumnsSummary
313
	 */
314
	protected $columnsSummary;
315
316
	/**
317
	 * @var bool
318
	 */
319
	protected $auto_submit = TRUE;
320
321
	/**
322
	 * @var Filter\SubmitButton|NULL
323
	 */
324
	protected $filter_submit_button = NULL;
325
326
327
	/**
328
	 * @param Nette\ComponentModel\IContainer|NULL $parent
329
	 * @param string                               $name
330
	 */
331
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
332
	{
333
		parent::__construct($parent, $name);
334
335
		$this->monitor('Nette\Application\UI\Presenter');
336
337
		/**
338
		 * Try to find previous filters, pagination, per_page and other values in session
339
		 */
340
		$this->onRender[] = [$this, 'findSessionValues'];
341
342
		/**
343
		 * Find default filter values
344
		 */
345
		$this->onRender[] = [$this, 'findDefaultFilter'];
346
347
		/**
348
		 * Find default sort
349
		 */
350
		$this->onRender[] = [$this, 'findDefaultSort'];
351
352
		/**
353
		 * Find default items per page
354
		 */
355
		$this->onRender[] = [$this, 'findDefaultPerPage'];
356
	}
357
358
359
	/**
360
	 * {inheritDoc}
361
	 * @return void
362
	 */
363
	public function attached($presenter)
364
	{
365
		parent::attached($presenter);
366
367
		if ($presenter instanceof Nette\Application\UI\Presenter) {
368
			/**
369
			 * Get session
370
			 */
371
			if ($this->remember_state) {
372
				$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...
373
			}
374
		}
375
	}
376
377
378
	/********************************************************************************
379
	 *                                  RENDERING                                   *
380
	 ********************************************************************************/
381
382
383
	/**
384
	 * Render template
385
	 * @return void
386
	 */
387
	public function render()
388
	{
389
		/**
390
		 * Check whether datagrid has set some columns, initiated data source, etc
391
		 */
392
		if (!($this->dataModel instanceof DataModel)) {
393
			throw new DataGridException('You have to set a data source first.');
394
		}
395
396
		if (empty($this->columns)) {
397
			throw new DataGridException('You have to add at least one column.');
398
		}
399
400
		$this->getTemplate()->setTranslator($this->getTranslator());
401
402
		/**
403
		 * Invoke possible events
404
		 */
405
		$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...
406
407
		/**
408
		 * Prepare data for rendering (datagrid may render just one item)
409
		 */
410
		$rows = [];
411
412
		if (!empty($this->redraw_item)) {
413
			$items = $this->dataModel->filterRow($this->redraw_item);
414
		} else {
415
			$items = Nette\Utils\Callback::invokeArgs(
416
				[$this->dataModel, 'filterData'],
417
				[
418
					$this->getPaginator(),
419
					$this->createSorting($this->sort, $this->sort_callback),
420
					$this->assableFilters()
421
				]
422
			);
423
		}
424
425
		$callback = $this->rowCallback ?: NULL;
426
		$hasGroupActionOnRows = FALSE;
427
428
		foreach ($items as $item) {
429
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
430
431
			if (!$hasGroupActionOnRows && $row->hasGroupAction()){
432
				$hasGroupActionOnRows = TRUE;
433
			}
434
			
435
			if ($callback) {
436
				$callback($item, $row->getControl());
437
			}
438
439
			/**
440
			 * Walkaround for item snippet - snippet is the <tr> element and its class has to be also updated
441
			 */
442
			if (!empty($this->redraw_item)) {
443
				$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...
444
				$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...
445
			}
446
		}
447
448
		if ($hasGroupActionOnRows){
449
			$hasGroupActionOnRows = $this->hasGroupActions();
450
		}
451
452
		if ($this->isTreeView()) {
453
			$this->getTemplate()->add('tree_view_has_children_column', $this->tree_view_has_children_column);
454
		}
455
456
		$this->getTemplate()->add('rows', $rows);
457
458
		$this->getTemplate()->add('columns', $this->getColumns());
459
		$this->getTemplate()->add('actions', $this->actions);
460
		$this->getTemplate()->add('exports', $this->exports);
461
		$this->getTemplate()->add('filters', $this->filters);
462
		$this->getTemplate()->add('toolbar_buttons', $this->toolbar_buttons);
463
464
		$this->getTemplate()->add('filter_active', $this->isFilterActive());
465
		$this->getTemplate()->add('original_template', $this->getOriginalTemplateFile());
466
		//$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...
467
		$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...
468
		$this->getTemplate()->add('items_detail', $this->items_detail);
469
		$this->getTemplate()->add('columns_visibility', $this->columns_visibility);
470
		$this->getTemplate()->add('columnsSummary', $this->columnsSummary);
471
472
		$this->getTemplate()->add('inlineEdit', $this->inlineEdit);
473
		$this->getTemplate()->add('inlineAdd', $this->inlineAdd);
474
475
		$this->getTemplate()->add('hasGroupActionOnRows', $hasGroupActionOnRows);
476
477
		/**
478
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
479
		 */
480
		$this->getTemplate()->add('filter', $this['filter']);
481
482
		/**
483
		 * Set template file and render it
484
		 */
485
		$this->getTemplate()->setFile($this->getTemplateFile());
486
		$this->getTemplate()->render();
487
	}
488
489
490
	/********************************************************************************
491
	 *                                 ROW CALLBACK                                 *
492
	 ********************************************************************************/
493
494
495
	/**
496
	 * Each row can be modified with user callback
497
	 * @param  callable  $callback
498
	 * @return static
499
	 */
500
	public function setRowCallback(callable $callback)
501
	{
502
		$this->rowCallback = $callback;
503
504
		return $this;
505
	}
506
507
508
	/********************************************************************************
509
	 *                                 DATA SOURCE                                  *
510
	 ********************************************************************************/
511
512
513
	/**
514
	 * By default ID, you can change that
515
	 * @param string $primary_key
516
	 * @return static
517
	 */
518
	public function setPrimaryKey($primary_key)
519
	{
520
		if ($this->dataModel instanceof DataModel) {
521
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
522
		}
523
524
		$this->primary_key = $primary_key;
525
526
		return $this;
527
	}
528
529
530
	/**
531
	 * Set Grid data source
532
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
533
	 * @return static
534
	 */
535
	public function setDataSource($source)
536
	{
537
		$this->dataModel = new DataModel($source, $this->primary_key);
538
539
		return $this;
540
	}
541
542
543
	/********************************************************************************
544
	 *                                  TEMPLATING                                  *
545
	 ********************************************************************************/
546
547
548
	/**
549
	 * Set custom template file to render
550
	 * @param string $template_file
551
	 * @return static
552
	 */
553
	public function setTemplateFile($template_file)
554
	{
555
		$this->template_file = $template_file;
556
557
		return $this;
558
	}
559
560
561
	/**
562
	 * Get DataGrid template file
563
	 * @return string
564
	 * @return static
565
	 */
566
	public function getTemplateFile()
567
	{
568
		return $this->template_file ?: $this->getOriginalTemplateFile();
569
	}
570
571
572
	/**
573
	 * Get DataGrid original template file
574
	 * @return string
575
	 */
576
	public function getOriginalTemplateFile()
577
	{
578
		return __DIR__.'/templates/datagrid.latte';
579
	}
580
581
582
	/**
583
	 * Tell datagrid wheteher to use or not happy components
584
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
585
	 * @return void|bool
586
	 */
587
	public function useHappyComponents($use = NULL)
588
	{
589
		if (NULL === $use) {
590
			return $this->use_happy_components;
591
		}
592
593
		$this->use_happy_components = (bool) $use;
594
	}
595
596
597
	/********************************************************************************
598
	 *                                   SORTING                                    *
599
	 ********************************************************************************/
600
601
602
	/**
603
	 * Set default sorting
604
	 * @param array $sort
605
	 * @return static
606
	 */
607
	public function setDefaultSort($sort)
608
	{
609
		if (is_string($sort)) {
610
			$sort = [$sort => 'ASC'];
611
		} else {
612
			$sort = (array) $sort;
613
		}
614
615
		$this->default_sort = $sort;
616
617
		return $this;
618
	}
619
620
621
	/**
622
	 * User may set default sorting, apply it
623
	 * @return void
624
	 */
625
	public function findDefaultSort()
626
	{
627
		if (!empty($this->sort)) {
628
			return;
629
		}
630
631
		if (!empty($this->default_sort)) {
632
			$this->sort = $this->default_sort;
633
		}
634
635
		$this->saveSessionData('_grid_sort', $this->sort);
636
	}
637
638
639
	/**
640
	 * Set grido to be sortable
641
	 * @param bool $sortable
642
	 * @return static
643
	 */
644
	public function setSortable($sortable = TRUE)
645
	{
646
		if ($this->getItemsDetail()) {
647
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
648
		}
649
650
		$this->sortable = (bool) $sortable;
651
652
		return $this;
653
	}
654
655
656
	/**
657
	 * Set sortable handle
658
	 * @param string $handler
659
	 * @return static
660
	 */
661
	public function setSortableHandler($handler = 'sort!')
662
	{
663
		$this->sortable_handler = (string) $handler;
664
665
		return $this;
666
	}
667
668
669
	/**
670
	 * Tell whether DataGrid is sortable
671
	 * @return bool
672
	 */
673
	public function isSortable()
674
	{
675
		return $this->sortable;
676
	}
677
678
	/**
679
	 * Return sortable handle name
680
	 * @return string
681
	 */
682
	public function getSortableHandler()
683
	{
684
		return $this->sortable_handler;
685
	}
686
687
688
	protected function createSorting(array $sort, $sort_callback)
689
	{
690
		foreach ($sort as $key => $order) {
691
			$column = $this->columns[$key];
692
			$sort = [$column->getSortingColumn() => $order];
693
		}
694
695
		return new Sorting($sort, $sort_callback);
696
	}
697
698
699
	/********************************************************************************
700
	 *                                  TREE VIEW                                   *
701
	 ********************************************************************************/
702
703
704
	/**
705
	 * Is tree view set?
706
	 * @return boolean
707
	 */
708
	public function isTreeView()
709
	{
710
		return (bool) $this->tree_view_children_callback;
711
	}
712
713
714
	/**
715
	 * Setting tree view
716
	 * @param callable $get_children_callback
717
	 * @param string|callable $tree_view_has_children_column
718
	 * @return static
719
	 */
720
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
721
	{
722
		if (!is_callable($get_children_callback)) {
723
			throw new DataGridException(
724
				'Parameters to method DataGrid::setTreeView must be of type callable'
725
			);
726
		}
727
728
		if (is_callable($tree_view_has_children_column)) {
729
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
730
			$tree_view_has_children_column = NULL;
731
		}
732
733
		$this->tree_view_children_callback = $get_children_callback;
734
		$this->tree_view_has_children_column = $tree_view_has_children_column;
735
736
		/**
737
		 * TUrn off pagination
738
		 */
739
		$this->setPagination(FALSE);
740
741
		/**
742
		 * Set tree view template file
743
		 */
744
		if (!$this->template_file) {
745
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
746
		}
747
748
		return $this;
749
	}
750
751
752
	/**
753
	 * Is tree view children callback set?
754
	 * @return boolean
755
	 */
756
	public function hasTreeViewChildrenCallback()
757
	{
758
		return is_callable($this->tree_view_has_children_callback);
759
	}
760
761
762
	/**
763
	 * @param  mixed $item
764
	 * @return boolean
765
	 */
766
	public function treeViewChildrenCallback($item)
767
	{
768
		return call_user_func($this->tree_view_has_children_callback, $item);
769
	}
770
771
772
	/********************************************************************************
773
	 *                                    COLUMNS                                   *
774
	 ********************************************************************************/
775
776
777
	/**
778
	 * Add text column with no other formating
779
	 * @param  string      $key
780
	 * @param  string      $name
781
	 * @param  string|null $column
782
	 * @return Column\ColumnText
783
	 */
784
	public function addColumnText($key, $name, $column = NULL)
785
	{
786
		$this->addColumnCheck($key);
787
		$column = $column ?: $key;
788
789
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
790
	}
791
792
793
	/**
794
	 * Add column with link
795
	 * @param  string      $key
796
	 * @param  string      $name
797
	 * @param  string|null $column
798
	 * @return Column\ColumnLink
799
	 */
800
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
801
	{
802
		$this->addColumnCheck($key);
803
		$column = $column ?: $key;
804
		$href = $href ?: $key;
805
806
		if (NULL === $params) {
807
			$params = [$this->primary_key];
808
		}
809
810
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
811
	}
812
813
814
	/**
815
	 * Add column with possible number formating
816
	 * @param  string      $key
817
	 * @param  string      $name
818
	 * @param  string|null $column
819
	 * @return Column\ColumnNumber
820
	 */
821
	public function addColumnNumber($key, $name, $column = NULL)
822
	{
823
		$this->addColumnCheck($key);
824
		$column = $column ?: $key;
825
826
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
827
	}
828
829
830
	/**
831
	 * Add column with date formating
832
	 * @param  string      $key
833
	 * @param  string      $name
834
	 * @param  string|null $column
835
	 * @return Column\ColumnDateTime
836
	 */
837
	public function addColumnDateTime($key, $name, $column = NULL)
838
	{
839
		$this->addColumnCheck($key);
840
		$column = $column ?: $key;
841
842
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
843
	}
844
845
846
	/**
847
	 * Add column status
848
	 * @param  string      $key
849
	 * @param  string      $name
850
	 * @param  string|null $column
851
	 * @return Column\ColumnStatus
852
	 */
853
	public function addColumnStatus($key, $name, $column = NULL)
854
	{
855
		$this->addColumnCheck($key);
856
		$column = $column ?: $key;
857
858
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
859
	}
860
861
862
	/**
863
	 * @param string $key
864
	 * @param Column\Column $column
865
	 * @return Column\Column
866
	 */
867
	protected function addColumn($key, Column\Column $column)
868
	{
869
		$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...
870
		return $this->columns[$key] = $column;
871
	}
872
873
874
	/**
875
	 * Return existing column
876
	 * @param  string $key
877
	 * @return Column\Column
878
	 * @throws DataGridException
879
	 */
880
	public function getColumn($key)
881
	{
882
		if (!isset($this->columns[$key])) {
883
			throw new DataGridException("There is no column at key [$key] defined.");
884
		}
885
886
		return $this->columns[$key];
887
	}
888
889
890
	/**
891
	 * Remove column
892
	 * @param string $key
893
	 * @return void
894
	 */
895
	public function removeColumn($key)
896
	{
897
		unset($this->columns[$key]);
898
	}
899
900
901
	/**
902
	 * Check whether given key already exists in $this->columns
903
	 * @param  string $key
904
	 * @throws DataGridException
905
	 */
906
	protected function addColumnCheck($key)
907
	{
908
		if (isset($this->columns[$key])) {
909
			throw new DataGridException("There is already column at key [$key] defined.");
910
		}
911
	}
912
913
914
	/********************************************************************************
915
	 *                                    ACTIONS                                   *
916
	 ********************************************************************************/
917
918
919
	/**
920
	 * Create action
921
	 * @param string     $key
922
	 * @param string     $name
923
	 * @param string     $href
924
	 * @param array|null $params
925
	 * @return Column\Action
926
	 */
927
	public function addAction($key, $name, $href = NULL, array $params = NULL)
928
	{
929
		$this->addActionCheck($key);
930
		$href = $href ?: $key;
931
932
		if (NULL === $params) {
933
			$params = [$this->primary_key];
934
		}
935
936
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
937
	}
938
939
940
	/**
941
	 * Create action callback
942
	 * @param string     $key
943
	 * @param string     $name
944
	 * @return Column\Action
945
	 */
946
	public function addActionCallback($key, $name, $callback = NULL)
947
	{
948
		$this->addActionCheck($key);
949
		$params = ['__id' => $this->primary_key];
950
951
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
952
953
		if ($callback) {
954
			if (!is_callable($callback)) {
955
				throw new DataGridException('ActionCallback callback has to be callable.');
956
			}
957
958
			$action->onClick[] = $callback;
959
		}
960
961
		return $action;
962
	}
963
964
965
	/**
966
	 * Get existing action
967
	 * @param  string       $key
968
	 * @return Column\Action
969
	 * @throws DataGridException
970
	 */
971
	public function getAction($key)
972
	{
973
		if (!isset($this->actions[$key])) {
974
			throw new DataGridException("There is no action at key [$key] defined.");
975
		}
976
977
		return $this->actions[$key];
978
	}
979
980
981
	/**
982
	 * Remove action
983
	 * @param string $key
984
	 * @return void
985
	 */
986
	public function removeAction($key)
987
	{
988
		unset($this->actions[$key]);
989
	}
990
991
992
	/**
993
	 * Check whether given key already exists in $this->filters
994
	 * @param  string $key
995
	 * @throws DataGridException
996
	 */
997
	protected function addActionCheck($key)
998
	{
999
		if (isset($this->actions[$key])) {
1000
			throw new DataGridException("There is already action at key [$key] defined.");
1001
		}
1002
	}
1003
1004
1005
	/********************************************************************************
1006
	 *                                    FILTERS                                   *
1007
	 ********************************************************************************/
1008
1009
1010
	/**
1011
	 * Add filter fot text search
1012
	 * @param string       $key
1013
	 * @param string       $name
1014
	 * @param array|string $columns
1015
	 * @return Filter\FilterText
1016
	 * @throws DataGridException
1017
	 */
1018
	public function addFilterText($key, $name, $columns = NULL)
1019
	{
1020
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
1021
1022
		if (!is_array($columns)) {
1023
			throw new DataGridException("Filter Text can except only array or string.");
1024
		}
1025
1026
		$this->addFilterCheck($key);
1027
1028
		return $this->filters[$key] = new Filter\FilterText($this, $key, $name, $columns);
1029
	}
1030
1031
1032
	/**
1033
	 * Add select box filter
1034
	 * @param string $key
1035
	 * @param string $name
1036
	 * @param array  $options
1037
	 * @param string $column
1038
	 * @return Filter\FilterSelect
1039
	 * @throws DataGridException
1040
	 */
1041 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...
1042
	{
1043
		$column = $column ?: $key;
1044
1045
		if (!is_string($column)) {
1046
			throw new DataGridException("Filter Select can only filter in one column.");
1047
		}
1048
1049
		$this->addFilterCheck($key);
1050
1051
		return $this->filters[$key] = new Filter\FilterSelect($this, $key, $name, $options, $column);
1052
	}
1053
1054
1055
	/**
1056
	 * Add multi select box filter
1057
	 * @param string $key
1058
	 * @param string $name
1059
	 * @param array  $options
1060
	 * @param string $column
1061
	 * @return Filter\FilterSelect
1062
	 * @throws DataGridException
1063
	 */
1064 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...
1065
	{
1066
		$column = $column ?: $key;
1067
1068
		if (!is_string($column)) {
1069
			throw new DataGridException("Filter MultiSelect can only filter in one column.");
1070
		}
1071
1072
		$this->addFilterCheck($key);
1073
1074
		return $this->filters[$key] = new Filter\FilterMultiSelect($this, $key, $name, $options, $column);
1075
	}
1076
1077
1078
	/**
1079
	 * Add datepicker filter
1080
	 * @param string $key
1081
	 * @param string $name
1082
	 * @param string $column
1083
	 * @return Filter\FilterDate
1084
	 * @throws DataGridException
1085
	 */
1086 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...
1087
	{
1088
		$column = $column ?: $key;
1089
1090
		if (!is_string($column)) {
1091
			throw new DataGridException("FilterDate can only filter in one column.");
1092
		}
1093
1094
		$this->addFilterCheck($key);
1095
1096
		return $this->filters[$key] = new Filter\FilterDate($this, $key, $name, $column);
1097
	}
1098
1099
1100
	/**
1101
	 * Add range filter (from - to)
1102
	 * @param string $key
1103
	 * @param string $name
1104
	 * @param string $column
1105
	 * @return Filter\FilterRange
1106
	 * @throws DataGridException
1107
	 */
1108 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...
1109
	{
1110
		$column = $column ?: $key;
1111
1112
		if (!is_string($column)) {
1113
			throw new DataGridException("FilterRange can only filter in one column.");
1114
		}
1115
1116
		$this->addFilterCheck($key);
1117
1118
		return $this->filters[$key] = new Filter\FilterRange($this, $key, $name, $column, $name_second);
1119
	}
1120
1121
1122
	/**
1123
	 * Add datepicker filter (from - to)
1124
	 * @param string $key
1125
	 * @param string $name
1126
	 * @param string $column
1127
	 * @return Filter\FilterDateRange
1128
	 * @throws DataGridException
1129
	 */
1130 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...
1131
	{
1132
		$column = $column ?: $key;
1133
1134
		if (!is_string($column)) {
1135
			throw new DataGridException("FilterDateRange can only filter in one column.");
1136
		}
1137
1138
		$this->addFilterCheck($key);
1139
1140
		return $this->filters[$key] = new Filter\FilterDateRange($this, $key, $name, $column, $name_second);
1141
	}
1142
1143
1144
	/**
1145
	 * Check whether given key already exists in $this->filters
1146
	 * @param  string $key
1147
	 * @throws DataGridException
1148
	 */
1149
	protected function addFilterCheck($key)
1150
	{
1151
		if (isset($this->filters[$key])) {
1152
			throw new DataGridException("There is already action at key [$key] defined.");
1153
		}
1154
	}
1155
1156
1157
	/**
1158
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1159
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1160
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1161
	 */
1162
	public function assableFilters()
1163
	{
1164
		foreach ($this->filter as $key => $value) {
1165
			if (!isset($this->filters[$key])) {
1166
				$this->deleteSesssionData($key);
1167
1168
				continue;
1169
			}
1170
1171
			if (is_array($value) || $value instanceof \Traversable) {
1172
				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...
1173
					$this->filters[$key]->setValue($value);
1174
				}
1175
			} else {
1176
				if ($value !== '' && $value !== NULL) {
1177
					$this->filters[$key]->setValue($value);
1178
				}
1179
			}
1180
		}
1181
1182
		foreach ($this->columns as $key => $column) {
1183
			if (isset($this->sort[$key])) {
1184
				$column->setSort($this->sort);
1185
			}
1186
		}
1187
1188
		return $this->filters;
1189
	}
1190
1191
1192
	/**
1193
	 * Remove filter
1194
	 * @param string $key
1195
	 * @return void
1196
	 */
1197
	public function removeFilter($key)
1198
	{
1199
		unset($this->filters[$key]);
1200
	}
1201
1202
1203
	/**
1204
	 * Get defined filter
1205
	 * @param  string $key
1206
	 * @return Filter\Filter
1207
	 */
1208
	public function getFilter($key)
1209
	{
1210
		if (!isset($this->filters[$key])) {
1211
			throw new DataGridException("Filter [{$key}] is not defined");
1212
		}
1213
1214
		return $this->filters[$key];
1215
	}
1216
1217
1218
	/********************************************************************************
1219
	 *                                  FILTERING                                   *
1220
	 ********************************************************************************/
1221
1222
1223
	/**
1224
	 * Is filter active?
1225
	 * @return boolean
1226
	 */
1227
	public function isFilterActive()
1228
	{
1229
		$is_filter = ArraysHelper::testTruthy($this->filter);
1230
1231
		return ($is_filter) || $this->force_filter_active;
1232
	}
1233
1234
1235
	/**
1236
	 * Tell that filter is active from whatever reasons
1237
	 * return static
1238
	 */
1239
	public function setFilterActive()
1240
	{
1241
		$this->force_filter_active = TRUE;
1242
1243
		return $this;
1244
	}
1245
1246
1247
	/**
1248
	 * Set filter values (force - overwrite user data)
1249
	 * @param array $filter
1250
	 * @return static
1251
	 */
1252
	public function setFilter(array $filter)
1253
	{
1254
		$this->filter = $filter;
1255
1256
		$this->saveSessionData('_grid_has_filtered', 1);
1257
1258
		return $this;
1259
	}
1260
1261
1262
	/**
1263
	 * If we want to sent some initial filter
1264
	 * @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...
1265
	 * @param bool  $use_on_reset
1266
	 * @return static
1267
	 */
1268
	public function setDefaultFilter(array $default_filter, $use_on_reset = TRUE)
1269
	{
1270
		foreach ($default_filter as $key => $value) {
1271
			$filter = $this->getFilter($key);
1272
1273
			if (!$filter) {
1274
				throw new DataGridException("Can not set default value to nonexisting filter [$key]");
1275
			}
1276
1277
			if ($filter instanceof Filter\FilterMultiSelect && !is_array($value)) {
1278
				throw new DataGridException(
1279
					"Default value of filter [$key] - MultiSelect has to be an array"
1280
				);
1281
			}
1282
1283
			if ($filter instanceof Filter\FilterRange || $filter instanceof Filter\FilterDateRange) {
1284
				if (!is_array($value)) {
1285
					throw new DataGridException(
1286
						"Default value of filter [$key] - Range/DateRange has to be an array [from/to => ...]"
1287
					);
1288
				}
1289
1290
				$temp = $value;
1291
				unset($temp['from'], $temp['to']);
1292
1293
				if (!empty($temp)) {
1294
					throw new DataGridException(
1295
						"Default value of filter [$key] - Range/DateRange can contain only [from/to => ...] values"
1296
					);
1297
				}
1298
			}
1299
		}
1300
1301
		$this->default_filter = $default_filter;
1302
		$this->default_filter_use_on_reset = (bool) $use_on_reset;
1303
1304
		return $this;
1305
	}
1306
1307
1308
	/**
1309
	 * User may set default filter, find it
1310
	 * @return void
1311
	 */
1312
	public function findDefaultFilter()
1313
	{
1314
		if (!empty($this->filter)) {
1315
			return;
1316
		}
1317
1318
		if ($this->getSessionData('_grid_has_filtered')) {
1319
			return;
1320
		}
1321
1322
		if (!empty($this->default_filter)) {
1323
			$this->filter = $this->default_filter;
1324
		}
1325
1326
		foreach ($this->filter as $key => $value) {
1327
			$this->saveSessionData($key, $value);
1328
		}
1329
	}
1330
1331
1332
	/**
1333
	 * FilterAndGroupAction form factory
1334
	 * @return Form
1335
	 */
1336
	public function createComponentFilter()
1337
	{
1338
		$form = new Form($this, 'filter');
1339
1340
		$form->setMethod(static::$form_method);
1341
1342
		$form->setTranslator($this->getTranslator());
1343
1344
		/**
1345
		 * InlineEdit part
1346
		 */
1347
		$inline_edit_container = $form->addContainer('inline_edit');
1348
1349 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...
1350
			$inline_edit_container->addSubmit('submit', 'ublaboo_datagrid.save');
1351
			$inline_edit_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1352
				->setValidationScope(FALSE);
1353
1354
			$this->inlineEdit->onControlAdd($inline_edit_container);
1355
		}
1356
1357
		/**
1358
		 * InlineAdd part
1359
		 */
1360
		$inline_add_container = $form->addContainer('inline_add');
1361
1362 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...
1363
			$inline_add_container->addSubmit('submit', 'ublaboo_datagrid.save');
1364
			$inline_add_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1365
				->setValidationScope(FALSE)
1366
				->setAttribute('data-datagrid-cancel-inline-add', TRUE);
1367
1368
			$this->inlineAdd->onControlAdd($inline_add_container);
1369
		}
1370
1371
		/**
1372
		 * ItemDetail form part
1373
		 */
1374
		$items_detail_form = $this->getItemDetailForm();
1375
1376
		if ($items_detail_form instanceof Nette\Forms\Container) {
1377
			$form['items_detail_form'] = $items_detail_form;
1378
		}
1379
1380
		/**
1381
		 * Filter part
1382
		 */
1383
		$filter_container = $form->addContainer('filter');
1384
1385
		foreach ($this->filters as $filter) {
1386
			$filter->addToFormContainer($filter_container);
1387
		}
1388
1389
		if (!$this->hasAutoSubmit()) {
1390
			$filter_container['submit'] = $this->getFilterSubmitButton();
1391
		}
1392
1393
		/**
1394
		 * Group action part
1395
		 */
1396
		$group_action_container = $form->addContainer('group_action');
1397
1398
		if ($this->hasGroupActions()) {
1399
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1400
		}
1401
1402
		$form->setDefaults(['filter' => $this->filter]);
1403
1404
		/**
1405
		 * Per page part
1406
		 */
1407
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1408
			->setTranslator(NULL);
1409
1410
		if (!$form->isSubmitted()) {
1411
			$form['per_page']->setValue($this->getPerPage());
1412
		}
1413
1414
		$form->addSubmit('per_page_submit', '');
1415
		
1416
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1417
1418
		return $form;
1419
	}
1420
1421
1422
	/**
1423
	 * Set $this->filter values after filter form submitted
1424
	 * @param  Form $form
1425
	 * @return void
1426
	 */
1427
	public function filterSucceeded(Form $form)
1428
	{
1429
		if ($this->snippets_set) {
1430
			return;
1431
		}
1432
1433
		$values = $form->getValues();
1434
1435
		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...
1436
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1437
				return;
1438
			}
1439
		}
1440
1441
		/**
1442
		 * Per page
1443
		 */
1444
		$this->saveSessionData('_grid_per_page', $values->per_page);
1445
		$this->per_page = $values->per_page;
1446
1447
		/**
1448
		 * Inline edit
1449
		 */
1450
		if (isset($form['inline_edit']) && isset($form['inline_edit']['submit']) && isset($form['inline_edit']['cancel'])) {
1451
			$edit = $form['inline_edit'];
1452
1453
			if ($edit['submit']->isSubmittedBy() || $edit['cancel']->isSubmittedBy()) {
1454
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1455
				$primary_where_column = $form->getHttpData(
1456
					Form::DATA_LINE,
1457
					'inline_edit[_primary_where_column]'
1458
				);
1459
1460
				if ($edit['submit']->isSubmittedBy()) {
1461
					$this->inlineEdit->onSubmit($id, $values->inline_edit);
1462
					$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...
1463
				} else {
1464
					$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...
1465
				}
1466
1467
				if ($edit['submit']->isSubmittedBy() && !empty($this->inlineEdit->onCustomRedraw)) {
1468
					$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...
1469
				} else {
1470
					$this->redrawItem($id, $primary_where_column);
1471
				}
1472
1473
				return;
1474
			}
1475
		}
1476
1477
		/**
1478
		 * Inline add
1479
		 */
1480
		if (isset($form['inline_add']) && isset($form['inline_add']['submit']) && isset($form['inline_add']['cancel'])) {
1481
			$add = $form['inline_add'];
1482
1483
			if ($add['submit']->isSubmittedBy() || $add['cancel']->isSubmittedBy()) {
1484
				if ($add['submit']->isSubmittedBy()) {
1485
					$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...
1486
1487
					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...
1488
						$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...
1489
					}
1490
				}
1491
1492
				return;
1493
			}
1494
		}
1495
1496
		/**
1497
		 * Filter itself
1498
		 */
1499
		$values = $values['filter'];
1500
1501
		foreach ($values as $key => $value) {
1502
			/**
1503
			 * Session stuff
1504
			 */
1505
			$this->saveSessionData($key, $value);
1506
1507
			/**
1508
			 * Other stuff
1509
			 */
1510
			$this->filter[$key] = $value;
1511
		}
1512
1513
		if (!empty($values)) {
1514
			$this->saveSessionData('_grid_has_filtered', 1);
1515
		}
1516
1517
		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...
1518
			$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...
1519
1520
			foreach ($this->columns as $key => $column) {
1521
				if ($column->isSortable()) {
1522
					$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...
1523
						'sort' => $column->getSortNext()
1524
					]);
1525
				}
1526
			}
1527
		}
1528
1529
		$this->reload();
1530
	}
1531
1532
1533
	/**
1534
	 * Should be datagrid filters rendered separately?
1535
	 * @param boolean $out
1536
	 * @return static
1537
	 */
1538
	public function setOuterFilterRendering($out = TRUE)
1539
	{
1540
		$this->outer_filter_rendering = (bool) $out;
1541
1542
		return $this;
1543
	}
1544
1545
1546
	/**
1547
	 * Are datagrid filters rendered separately?
1548
	 * @return boolean
1549
	 */
1550
	public function hasOuterFilterRendering()
1551
	{
1552
		return $this->outer_filter_rendering;
1553
	}
1554
1555
1556
	/**
1557
	 * Try to restore session stuff
1558
	 * @return void
1559
	 */
1560
	public function findSessionValues()
1561
	{
1562
		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...
1563
			return;
1564
		}
1565
1566
		if (!$this->remember_state) {
1567
			return;
1568
		}
1569
1570
		if ($page = $this->getSessionData('_grid_page')) {
1571
			$this->page = $page;
1572
		}
1573
1574
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1575
			$this->per_page = $per_page;
1576
		}
1577
1578
		if ($sort = $this->getSessionData('_grid_sort')) {
1579
			$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...
1580
		}
1581
1582
		foreach ($this->getSessionData() as $key => $value) {
1583
			$other_session_keys = [
1584
				'_grid_per_page',
1585
				'_grid_sort',
1586
				'_grid_page',
1587
				'_grid_has_filtered',
1588
				'_grid_hidden_columns',
1589
				'_grid_hidden_columns_manipulated'
1590
			];
1591
1592
			if (!in_array($key, $other_session_keys)) {
1593
				$this->filter[$key] = $value;
1594
			}
1595
		}
1596
1597
		/**
1598
		 * When column is sorted via custom callback, apply it
1599
		 */
1600
		if (empty($this->sort_callback) && !empty($this->sort)) {
1601
			foreach ($this->sort as $key => $order) {
1602
				$column = $this->getColumn($key);
1603
1604
				if ($column && $column->isSortable() && is_callable($column->getSortableCallback())) {
1605
					$this->sort_callback = $column->getSortableCallback();
1606
				}
1607
			}
1608
		}
1609
	}
1610
1611
1612
	/********************************************************************************
1613
	 *                                    EXPORTS                                   *
1614
	 ********************************************************************************/
1615
1616
1617
	/**
1618
	 * Add export of type callback
1619
	 * @param string $text
1620
	 * @param callable $callback
1621
	 * @param boolean $filtered
1622
	 * @return Export\Export
1623
	 */
1624
	public function addExportCallback($text, $callback, $filtered = FALSE)
1625
	{
1626
		if (!is_callable($callback)) {
1627
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1628
		}
1629
1630
		return $this->addToExports(new Export\Export($this, $text, $callback, $filtered));
1631
	}
1632
1633
1634
	/**
1635
	 * Add already implemented csv export
1636
	 * @param string $text
1637
	 * @param string $csv_file_name
1638
	 * @param string|null $output_encoding
1639
	 * @param string|null $delimiter
1640
	 * @param bool $include_bom
1641
	 * @return Export\Export
1642
	 */
1643 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...
1644
	{
1645
		return $this->addToExports(new Export\ExportCsv(
1646
			$this,
1647
			$text,
1648
			$csv_file_name,
1649
			FALSE,
1650
			$output_encoding,
1651
			$delimiter,
1652
			$include_bom
1653
		));
1654
	}
1655
1656
1657
	/**
1658
	 * Add already implemented csv export, but for filtered data
1659
	 * @param string $text
1660
	 * @param string $csv_file_name
1661
	 * @param string|null $output_encoding
1662
	 * @param string|null $delimiter
1663
	 * @param bool $include_bom
1664
	 * @return Export\Export
1665
	 */
1666 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...
1667
	{
1668
		return $this->addToExports(new Export\ExportCsv(
1669
			$this,
1670
			$text,
1671
			$csv_file_name,
1672
			TRUE,
1673
			$output_encoding,
1674
			$delimiter,
1675
			$include_bom
1676
		));
1677
	}
1678
1679
1680
	/**
1681
	 * Add export to array
1682
	 * @param Export\Export $export
1683
	 * @return Export\Export
1684
	 */
1685
	protected function addToExports(Export\Export $export)
1686
	{
1687
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1688
1689
		$export->setLink(new Link($this, 'export!', ['id' => $id]));
1690
1691
		return $this->exports[$id] = $export;
1692
	}
1693
1694
1695
	public function resetExportsLinks()
1696
	{
1697
		foreach ($this->exports as $id => $export) {
1698
			$export->setLink(new Link($this, 'export!', ['id' => $id]));
1699
		}
1700
	}
1701
1702
1703
	/********************************************************************************
1704
	 *                                TOOLBAR BUTTONS                               *
1705
	 ********************************************************************************/
1706
1707
1708
	/**
1709
	 * Add toolbar button
1710
	 * @param string $href
1711
	 * @param string $text
1712
	 * @param array  $params
1713
	 * @return ToolbarButton
1714
	 */
1715
	public function addToolbarButton($href, $text = '', $params = [])
1716
	{
1717
		$button = new ToolbarButton($this, $href, $text, $params);
1718
1719
		return $this->toolbar_buttons[] = $button;
1720
	}
1721
1722
1723
	/********************************************************************************
1724
	 *                                 GROUP ACTIONS                                *
1725
	 ********************************************************************************/
1726
1727
1728
	/**
1729
	 * Alias for add group select action
1730
	 * @param string $title
1731
	 * @param array  $options
1732
	 * @return GroupAction\GroupAction
1733
	 */
1734
	public function addGroupAction($title, $options = [])
1735
	{
1736
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1737
	}
1738
1739
	/**
1740
	 * Add group action (select box)
1741
	 * @param string $title
1742
	 * @param array  $options
1743
	 * @return GroupAction\GroupAction
1744
	 */
1745
	public function addGroupSelectAction($title, $options = [])
1746
	{
1747
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1748
	}
1749
1750
	/**
1751
	 * Add group action (text input)
1752
	 * @param string $title
1753
	 * @return GroupAction\GroupAction
1754
	 */
1755
	public function addGroupTextAction($title)
1756
	{
1757
		return $this->getGroupActionCollection()->addGroupTextAction($title);
1758
	}
1759
1760
	/**
1761
	 * Get collection of all group actions
1762
	 * @return GroupAction\GroupActionCollection
1763
	 */
1764
	public function getGroupActionCollection()
1765
	{
1766
		if (!$this->group_action_collection) {
1767
			$this->group_action_collection = new GroupAction\GroupActionCollection($this);
1768
		}
1769
1770
		return $this->group_action_collection;
1771
	}
1772
1773
1774
	/**
1775
	 * Has datagrid some group actions?
1776
	 * @return boolean
1777
	 */
1778
	public function hasGroupActions()
1779
	{
1780
		return (bool) $this->group_action_collection;
1781
	}
1782
1783
1784
	/********************************************************************************
1785
	 *                                   HANDLERS                                   *
1786
	 ********************************************************************************/
1787
1788
1789
	/**
1790
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1791
	 * @param  int  $page
1792
	 * @return void
1793
	 */
1794
	public function handlePage($page)
1795
	{
1796
		/**
1797
		 * Session stuff
1798
		 */
1799
		$this->page = $page;
1800
		$this->saveSessionData('_grid_page', $page);
1801
1802
		$this->reload(['table']);
1803
	}
1804
1805
1806
	/**
1807
	 * Handler for sorting
1808
	 * @param array $sort
1809
	 * @return void
1810
	 */
1811
	public function handleSort(array $sort)
1812
	{
1813
		$new_sort = [];
1814
1815
		/**
1816
		 * Find apropirate column
1817
		 */
1818
		foreach ($sort as $key => $value) {
1819
			if (empty($this->columns[$key])) {
1820
				throw new DataGridException("Column <$key> not found");
1821
			}
1822
1823
			$column = $this->columns[$key];
1824
			$new_sort = [$key => $value];
1825
1826
			/**
1827
			 * Pagination may be reseted after sorting
1828
			 */
1829
			if ($column->sortableResetPagination()) {
1830
				$this->page = 1;
1831
				$this->saveSessionData('_grid_page', 1);
1832
			}
1833
1834
			/**
1835
			 * Custom sorting callback may be applied
1836
			 */
1837
			if ($column->getSortableCallback()) {
1838
				$this->sort_callback = $column->getSortableCallback();
1839
			}
1840
		}
1841
1842
		/**
1843
		 * Session stuff
1844
		 */
1845
		$this->sort = $new_sort;
1846
		$this->saveSessionData('_grid_sort', $this->sort);
1847
1848
		$this->reload(['table']);
1849
	}
1850
1851
1852
	/**
1853
	 * handler for reseting the filter
1854
	 * @return void
1855
	 */
1856
	public function handleResetFilter()
1857
	{
1858
		/**
1859
		 * Session stuff
1860
		 */
1861
		$this->deleteSesssionData('_grid_page');
1862
1863
		if ($this->default_filter_use_on_reset) {
1864
			$this->deleteSesssionData('_grid_has_filtered');
1865
		}
1866
1867
		foreach ($this->getSessionData() as $key => $value) {
1868
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_has_filtered'])) {
1869
				$this->deleteSesssionData($key);
1870
			}
1871
		}
1872
1873
		$this->filter = [];
1874
1875
		$this->reload(['grid']);
1876
	}
1877
1878
1879
	/**
1880
	 * Handler for export
1881
	 * @param  int $id Key for particular export class in array $this->exports
1882
	 * @return void
1883
	 */
1884
	public function handleExport($id)
1885
	{
1886
		if (!isset($this->exports[$id])) {
1887
			throw new Nette\Application\ForbiddenRequestException;
1888
		}
1889
1890
		if (!empty($this->columns_export_order)) {
1891
			$this->setColumnsOrder($this->columns_export_order);
1892
		}
1893
1894
		$export = $this->exports[$id];
1895
1896
		if ($export->isFiltered()) {
1897
			$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...
1898
			$filter    = $this->assableFilters();
1899
		} else {
1900
			$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...
1901
			$filter    = [];
1902
		}
1903
1904
		if (NULL === $this->dataModel) {
1905
			throw new DataGridException('You have to set a data source first.');
1906
		}
1907
1908
		$rows = [];
1909
1910
		$items = Nette\Utils\Callback::invokeArgs(
1911
			[$this->dataModel, 'filterData'], [
1912
				NULL,
1913
				$this->createSorting($this->sort, $this->sort_callback),
1914
				$filter
1915
			]
1916
		);
1917
1918
		foreach ($items as $item) {
1919
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1920
		}
1921
1922
		if ($export instanceof Export\ExportCsv) {
1923
			$export->invoke($rows);
1924
		} else {
1925
			$export->invoke($items);
1926
		}
1927
1928
		if ($export->isAjax()) {
1929
			$this->reload();
1930
		}
1931
	}
1932
1933
1934
	/**
1935
	 * Handler for getting children of parent item (e.g. category)
1936
	 * @param  int $parent
1937
	 * @return void
1938
	 */
1939
	public function handleGetChildren($parent)
1940
	{
1941
		$this->setDataSource(
1942
			call_user_func($this->tree_view_children_callback, $parent)
1943
		);
1944
1945
		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...
1946
			$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...
1947
			$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...
1948
1949
			$this->redrawControl('items');
1950
1951
			$this->onRedraw();
1952
		} else {
1953
			$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\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, 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...
1954
		}
1955
	}
1956
1957
1958
	/**
1959
	 * Handler for getting item detail
1960
	 * @param  mixed $id
1961
	 * @return void
1962
	 */
1963
	public function handleGetItemDetail($id)
1964
	{
1965
		$this->getTemplate()->add('toggle_detail', $id);
1966
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1967
1968
		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...
1969
			$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...
1970
			$this->redrawControl('items');
1971
1972
			$this->onRedraw();
1973
		} else {
1974
			$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\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, 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...
1975
		}
1976
	}
1977
1978
1979
	/**
1980
	 * Handler for inline editing
1981
	 * @param  mixed $id
1982
	 * @param  mixed $key
1983
	 * @return void
1984
	 */
1985
	public function handleEdit($id, $key)
1986
	{
1987
		$column = $this->getColumn($key);
1988
		$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...
1989
1990
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1991
	}
1992
1993
1994
	/**
1995
	 * Redraw $this
1996
	 * @return void
1997
	 */
1998
	public function reload($snippets = [])
1999
	{
2000
		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...
2001
			$this->redrawControl('tbody');
2002
			$this->redrawControl('pagination');
2003
2004
			/**
2005
			 * manualy reset exports links...
2006
			 */
2007
			$this->resetExportsLinks();
2008
			$this->redrawControl('exports');
2009
2010
			foreach ($snippets as $snippet) {
2011
				$this->redrawControl($snippet);
2012
			}
2013
2014
			$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...
2015
2016
			$this->onRedraw();
2017
		} else {
2018
			$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\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, 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...
2019
		}
2020
	}
2021
2022
2023
	/**
2024
	 * Handler for column status
2025
	 * @param  string $id
2026
	 * @param  string $key
2027
	 * @param  string $value
2028
	 * @return void
2029
	 */
2030
	public function handleChangeStatus($id, $key, $value)
2031
	{
2032
		if (empty($this->columns[$key])) {
2033
			throw new DataGridException("ColumnStatus[$key] does not exist");
2034
		}
2035
2036
		$this->columns[$key]->onChange($id, $value);
2037
	}
2038
2039
2040
	/**
2041
	 * Redraw just one row via ajax
2042
	 * @param  int   $id
2043
	 * @param  mixed $primary_where_column
2044
	 * @return void
2045
	 */
2046
	public function redrawItem($id, $primary_where_column = NULL)
2047
	{
2048
		$this->snippets_set = TRUE;
2049
2050
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
2051
2052
		$this->redrawControl('items');
2053
2054
		$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...
2055
2056
		$this->onRedraw();
2057
	}
2058
2059
2060
	/**
2061
	 * Tell datagrid to display all columns
2062
	 * @return void
2063
	 */
2064
	public function handleShowAllColumns()
2065
	{
2066
		$this->deleteSesssionData('_grid_hidden_columns');
2067
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2068
2069
		$this->redrawControl();
2070
2071
		$this->onRedraw();
2072
	}
2073
2074
2075
	/**
2076
	 * Tell datagrid to display default columns
2077
	 * @return void
2078
	 */
2079
	public function handleShowDefaultColumns()
2080
	{
2081
		$this->deleteSesssionData('_grid_hidden_columns');
2082
		$this->saveSessionData('_grid_hidden_columns_manipulated', FALSE);
2083
2084
		$this->redrawControl();
2085
2086
		$this->onRedraw();
2087
	}
2088
2089
2090
	/**
2091
	 * Reveal particular column
2092
	 * @param  string $column
2093
	 * @return void
2094
	 */
2095 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...
2096
	{
2097
		$columns = $this->getSessionData('_grid_hidden_columns');
2098
2099
		if (!empty($columns)) {
2100
			$pos = array_search($column, $columns);
2101
2102
			if ($pos !== FALSE) {
2103
				unset($columns[$pos]);
2104
			}
2105
		}
2106
2107
		$this->saveSessionData('_grid_hidden_columns', $columns);
2108
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2109
2110
		$this->redrawControl();
2111
2112
		$this->onRedraw();
2113
	}
2114
2115
2116
	/**
2117
	 * Notice datagrid to not display particular columns
2118
	 * @param  string $column
2119
	 * @return void
2120
	 */
2121 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...
2122
	{
2123
		/**
2124
		 * Store info about hiding a column to session
2125
		 */
2126
		$columns = $this->getSessionData('_grid_hidden_columns');
2127
2128
		if (empty($columns)) {
2129
			$columns = [$column];
2130
		} else if (!in_array($column, $columns)) {
2131
			array_push($columns, $column);
2132
		}
2133
2134
		$this->saveSessionData('_grid_hidden_columns', $columns);
2135
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2136
2137
		$this->redrawControl();
2138
2139
		$this->onRedraw();
2140
	}
2141
2142
2143
	public function handleActionCallback($__key, $__id)
2144
	{
2145
		$action = $this->getAction($__key);
2146
2147
		if (!($action instanceof Column\ActionCallback)) {
2148
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
2149
		}
2150
2151
		$action->onClick($__id);
2152
	}
2153
2154
2155
	/********************************************************************************
2156
	 *                                  PAGINATION                                  *
2157
	 ********************************************************************************/
2158
2159
2160
	/**
2161
	 * Set options of select "items_per_page"
2162
	 * @param array $items_per_page_list
2163
	 * @return static
2164
	 */
2165
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
2166
	{
2167
		$this->items_per_page_list = $items_per_page_list;
2168
2169
		if ($include_all) {
2170
			$this->items_per_page_list[] = 'all';
2171
		}
2172
2173
		return $this;
2174
	}
2175
2176
2177
	/**
2178
	 * Set default "items per page" value in pagination select
2179
	 * @param $count
2180
	 * @return static
2181
	 */
2182
	public function setDefaultPerPage($count)
2183
	{
2184
		$this->default_per_page = $count;
2185
2186
		return $this;
2187
	}
2188
2189
2190
	/**
2191
	 * User may set default "items per page" value, apply it
2192
	 * @return void
2193
	 */
2194
	public function findDefaultPerPage()
2195
	{
2196
		if (!empty($this->per_page)) {
2197
			return;
2198
		}
2199
2200
		if (!empty($this->default_per_page)) {
2201
			$this->per_page = $this->default_per_page;
2202
		}
2203
2204
		$this->saveSessionData('_grid_per_page', $this->per_page);
2205
	}
2206
2207
2208
	/**
2209
	 * Paginator factory
2210
	 * @return Components\DataGridPaginator\DataGridPaginator
2211
	 */
2212
	public function createComponentPaginator()
2213
	{
2214
		/**
2215
		 * Init paginator
2216
		 */
2217
		$component = new Components\DataGridPaginator\DataGridPaginator(
2218
			$this->getTranslator(),
2219
			static::$icon_prefix
2220
		);
2221
		$paginator = $component->getPaginator();
2222
2223
		$paginator->setPage($this->page);
2224
		$paginator->setItemsPerPage($this->getPerPage());
2225
2226
		return $component;
2227
	}
2228
2229
2230
	/**
2231
	 * Get parameter per_page
2232
	 * @return int
2233
	 */
2234
	public function getPerPage()
2235
	{
2236
		$items_per_page_list = $this->getItemsPerPageList();
2237
2238
		$per_page = $this->per_page ?: reset($items_per_page_list);
2239
2240
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
2241
			$per_page = reset($items_per_page_list);
2242
		}
2243
2244
		return $per_page;
2245
	}
2246
2247
2248
	/**
2249
	 * Get associative array of items_per_page_list
2250
	 * @return array
2251
	 */
2252
	public function getItemsPerPageList()
2253
	{
2254
		if (empty($this->items_per_page_list)) {
2255
			$this->setItemsPerPageList([10, 20, 50], TRUE);
2256
		}
2257
2258
		$list = array_flip($this->items_per_page_list);
2259
2260
		foreach ($list as $key => $value) {
2261
			$list[$key] = $key;
2262
		}
2263
2264
		if (array_key_exists('all', $list)) {
2265
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
2266
		}
2267
2268
		return $list;
2269
	}
2270
2271
2272
	/**
2273
	 * Order Grid to "be paginated"
2274
	 * @param bool $do
2275
	 * @return static
2276
	 */
2277
	public function setPagination($do)
2278
	{
2279
		$this->do_paginate = (bool) $do;
2280
2281
		return $this;
2282
	}
2283
2284
2285
	/**
2286
	 * Tell whether Grid is paginated
2287
	 * @return bool
2288
	 */
2289
	public function isPaginated()
2290
	{
2291
		return $this->do_paginate;
2292
	}
2293
2294
2295
	/**
2296
	 * Return current paginator class
2297
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
2298
	 */
2299
	public function getPaginator()
2300
	{
2301
		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...
2302
			return $this['paginator'];
2303
		}
2304
2305
		return NULL;
2306
	}
2307
2308
2309
	/********************************************************************************
2310
	 *                                     I18N                                     *
2311
	 ********************************************************************************/
2312
2313
2314
	/**
2315
	 * Set datagrid translator
2316
	 * @param Nette\Localization\ITranslator $translator
2317
	 * @return static
2318
	 */
2319
	public function setTranslator(Nette\Localization\ITranslator $translator)
2320
	{
2321
		$this->translator = $translator;
2322
2323
		return $this;
2324
	}
2325
2326
2327
	/**
2328
	 * Get translator for datagrid
2329
	 * @return Nette\Localization\ITranslator
2330
	 */
2331
	public function getTranslator()
2332
	{
2333
		if (!$this->translator) {
2334
			$this->translator = new Localization\SimpleTranslator;
2335
		}
2336
2337
		return $this->translator;
2338
	}
2339
2340
2341
	/********************************************************************************
2342
	 *                                 COLUMNS ORDER                                *
2343
	 ********************************************************************************/
2344
2345
2346
	/**
2347
	 * Set order of datagrid columns
2348
	 * @param array $order
2349
	 * @return static
2350
	 */
2351
	public function setColumnsOrder($order)
2352
	{
2353
		$new_order = [];
2354
2355
		foreach ($order as $key) {
2356
			if (isset($this->columns[$key])) {
2357
				$new_order[$key] = $this->columns[$key];
2358
			}
2359
		}
2360
2361
		if (sizeof($new_order) === sizeof($this->columns)) {
2362
			$this->columns = $new_order;
2363
		} else {
2364
			throw new DataGridException('When changing columns order, you have to specify all columns');
2365
		}
2366
2367
		return $this;
2368
	}
2369
2370
2371
	/**
2372
	 * Columns order may be different for export and normal grid
2373
	 * @param array $order
2374
	 */
2375
	public function setColumnsExportOrder($order)
2376
	{
2377
		$this->columns_export_order = (array) $order;
2378
	}
2379
2380
2381
	/********************************************************************************
2382
	 *                                SESSION & URL                                 *
2383
	 ********************************************************************************/
2384
2385
2386
	/**
2387
	 * Find some unique session key name
2388
	 * @return string
2389
	 */
2390
	public function getSessionSectionName()
2391
	{
2392
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
2393
	}
2394
2395
2396
	/**
2397
	 * Should datagrid remember its filters/pagination/etc using session?
2398
	 * @param bool $remember
2399
	 * @return static
2400
	 */
2401
	public function setRememberState($remember = TRUE)
2402
	{
2403
		$this->remember_state = (bool) $remember;
2404
2405
		return $this;
2406
	}
2407
2408
2409
	/**
2410
	 * Should datagrid refresh url using history API?
2411
	 * @param bool $refresh
2412
	 * @return static
2413
	 */
2414
	public function setRefreshUrl($refresh = TRUE)
2415
	{
2416
		$this->refresh_url = (bool) $refresh;
2417
2418
2419
		return $this;
2420
	}
2421
2422
2423
	/**
2424
	 * Get session data if functionality is enabled
2425
	 * @param  string $key
2426
	 * @return mixed
2427
	 */
2428
	public function getSessionData($key = NULL, $default_value = NULL)
2429
	{
2430
		if (!$this->remember_state) {
2431
			return $key ? $default_value : [];
2432
		}
2433
2434
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2435
	}
2436
2437
2438
	/**
2439
	 * Save session data - just if it is enabled
2440
	 * @param  string $key
2441
	 * @param  mixed  $value
2442
	 * @return void
2443
	 */
2444
	public function saveSessionData($key, $value)
2445
	{
2446
		if ($this->remember_state) {
2447
			$this->grid_session->{$key} = $value;
2448
		}
2449
	}
2450
2451
2452
	/**
2453
	 * Delete session data
2454
	 * @return void
2455
	 */
2456
	public function deleteSesssionData($key)
2457
	{
2458
		unset($this->grid_session->{$key});
2459
	}
2460
2461
2462
	/********************************************************************************
2463
	 *                                  ITEM DETAIL                                 *
2464
	 ********************************************************************************/
2465
2466
2467
	/**
2468
	 * Get items detail parameters
2469
	 * @return array
2470
	 */
2471
	public function getItemsDetail()
2472
	{
2473
		return $this->items_detail;
2474
	}
2475
2476
2477
	/**
2478
	 * Items can have thair detail - toggled
2479
	 * @param mixed $detail callable|string|bool
2480
	 * @param bool|NULL $primary_where_column
2481
	 * @return Column\ItemDetail
2482
	 */
2483
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2484
	{
2485
		if ($this->isSortable()) {
2486
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2487
		}
2488
2489
		$this->items_detail = new Column\ItemDetail(
2490
			$this,
2491
			$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...
2492
		);
2493
2494
		if (is_string($detail)) {
2495
			/**
2496
			 * Item detail will be in separate template
2497
			 */
2498
			$this->items_detail->setType('template');
2499
			$this->items_detail->setTemplate($detail);
2500
2501
		} else if (is_callable($detail)) {
2502
			/**
2503
			 * Item detail will be rendered via custom callback renderer
2504
			 */
2505
			$this->items_detail->setType('renderer');
2506
			$this->items_detail->setRenderer($detail);
2507
2508
		} else if (TRUE === $detail) {
2509
			/**
2510
			 * Item detail will be rendered probably via block #detail
2511
			 */
2512
			$this->items_detail->setType('block');
2513
2514
		} else {
2515
			throw new DataGridException(
2516
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2517
			);
2518
		}
2519
2520
		return $this->items_detail;
2521
	}
2522
2523
2524
	/**
2525
	 * @param callable $callable_set_container 
2526
	 * @return static
2527
	 */
2528
	public function setItemsDetailForm(callable $callable_set_container)
2529
	{
2530
		if ($this->items_detail instanceof Column\ItemDetail) {
2531
			$this->items_detail->setForm(
2532
				new Utils\ItemDetailForm($callable_set_container)
2533
			);
2534
2535
			return $this;
2536
		}
2537
2538
		throw new DataGridException('Please set the ItemDetail first.');
2539
	}
2540
2541
2542
	/**
2543
	 * @return Nette\Forms\Container|NULL
2544
	 */
2545
	public function getItemDetailForm()
2546
	{
2547
		if ($this->items_detail instanceof Column\ItemDetail) {
2548
			return $this->items_detail->getForm();
2549
		}
2550
2551
		return NULL;
2552
	}
2553
2554
2555
	/********************************************************************************
2556
	 *                                ROW PRIVILEGES                                *
2557
	 ********************************************************************************/
2558
2559
2560
	/**
2561
	 * @param  callable $condition
2562
	 * @return void
2563
	 */
2564
	public function allowRowsGroupAction(callable $condition)
2565
	{
2566
		$this->row_conditions['group_action'] = $condition;
2567
	}
2568
2569
2570
	/**
2571
	 * @param  callable $condition
2572
	 * @return void
2573
	 */
2574
	public function allowRowsInlineEdit(callable $condition)
2575
	{
2576
		$this->row_conditions['inline_edit'] = $condition;
2577
	}
2578
2579
2580
	/**
2581
	 * @param  string   $key
2582
	 * @param  callable $condition
2583
	 * @return void
2584
	 */
2585
	public function allowRowsAction($key, callable $condition)
2586
	{
2587
		$this->row_conditions['action'][$key] = $condition;
2588
	}
2589
2590
2591
	/**
2592
	 * @param  string      $name
2593
	 * @param  string|null $key
2594
	 * @return bool|callable
2595
	 */
2596
	public function getRowCondition($name, $key = NULL)
2597
	{
2598
		if (!isset($this->row_conditions[$name])) {
2599
			return FALSE;
2600
		}
2601
2602
		$condition = $this->row_conditions[$name];
2603
2604
		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...
2605
			return $condition;
2606
		}
2607
2608
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2609
	}
2610
2611
2612
	/********************************************************************************
2613
	 *                               COLUMN CALLBACK                                *
2614
	 ********************************************************************************/
2615
2616
2617
	/**
2618
	 * @param  string   $key
2619
	 * @param  callable $callback
2620
	 * @return void
2621
	 */
2622
	public function addColumnCallback($key, callable $callback)
2623
	{
2624
		$this->column_callbacks[$key] = $callback;
2625
	}
2626
2627
2628
	/**
2629
	 * @param  string $key
2630
	 * @return callable|null
2631
	 */
2632
	public function getColumnCallback($key)
2633
	{
2634
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
2635
	}
2636
2637
2638
	/********************************************************************************
2639
	 *                                 INLINE EDIT                                  *
2640
	 ********************************************************************************/
2641
2642
2643
	/**
2644
	 * @return InlineEdit
2645
	 */
2646
	public function addInlineEdit($primary_where_column = NULL)
2647
	{
2648
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2649
2650
		return $this->inlineEdit;
2651
	}
2652
2653
2654
	/**
2655
	 * @return InlineEdit|null
2656
	 */
2657
	public function getInlineEdit()
2658
	{
2659
		return $this->inlineEdit;
2660
	}
2661
2662
2663
	/**
2664
	 * @param  mixed $id
2665
	 * @return void
2666
	 */
2667
	public function handleInlineEdit($id)
2668
	{
2669
		if ($this->inlineEdit) {
2670
			$this->inlineEdit->setItemId($id);
2671
2672
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2673
2674
			$this['filter']['inline_edit']->addHidden('_id', $id);
2675
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2676
2677
			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...
2678
				$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...
2679
			}
2680
2681
			$this->redrawItem($id, $primary_where_column);
2682
		}
2683
	}
2684
2685
2686
	/********************************************************************************
2687
	 *                                  INLINE ADD                                  *
2688
	 ********************************************************************************/
2689
2690
2691
	/**
2692
	 * @return InlineEdit
2693
	 */
2694
	public function addInlineAdd()
2695
	{
2696
		$this->inlineAdd = new InlineEdit($this);
2697
2698
		$this->inlineAdd
2699
			->setIcon('plus')
2700
			->setClass('btn btn-xs btn-default');
2701
2702
		return $this->inlineAdd;
2703
	}
2704
2705
2706
	/**
2707
	 * @return InlineEdit|null
2708
	 */
2709
	public function getInlineAdd()
2710
	{
2711
		return $this->inlineAdd;
2712
	}
2713
2714
2715
	/********************************************************************************
2716
	 *                               HIDEABLE COLUMNS                               *
2717
	 ********************************************************************************/
2718
2719
2720
	/**
2721
	 * Can datagrid hide colums?
2722
	 * @return boolean
2723
	 */
2724
	public function canHideColumns()
2725
	{
2726
		return (bool) $this->can_hide_columns;
2727
	}
2728
2729
2730
	/**
2731
	 * Order Grid to set columns hideable.
2732
	 * @return static
2733
	 */
2734
	public function setColumnsHideable()
2735
	{
2736
		$this->can_hide_columns = TRUE;
2737
2738
		return $this;
2739
	}
2740
2741
2742
	/********************************************************************************
2743
	 *                                COLUMNS SUMMARY                               *
2744
	 ********************************************************************************/
2745
2746
2747
	/**
2748
	 * Will datagrid show summary in the end?
2749
	 * @return bool
2750
	 */
2751
	public function hasColumnsSummary()
2752
	{
2753
		return $this->columnsSummary instanceof ColumnsSummary;
2754
	}
2755
2756
2757
	/**
2758
	 * Set columns to be summarized in the end.
2759
	 * @param  array  $columns
2760
	 * @return ColumnsSummary
2761
	 */
2762
	public function setColumnsSummary(array $columns)
2763
	{
2764
		$this->columnsSummary = new ColumnsSummary($this, $columns);
2765
2766
		return $this->columnsSummary;
2767
	}
2768
2769
2770
	/**
2771
	 * @return ColumnsSummary|NULL
2772
	 */
2773
	public function getColumnsSummary()
2774
	{
2775
		return $this->columnsSummary;
2776
	}
2777
2778
2779
	/********************************************************************************
2780
	 *                                   INTERNAL                                   *
2781
	 ********************************************************************************/
2782
2783
2784
	/**
2785
	 * Tell grid filters to by submitted automatically
2786
	 * @param bool $auto
2787
	 */
2788
	public function setAutoSubmit($auto = TRUE)
2789
	{
2790
		$this->auto_submit = (bool) $auto;
2791
2792
		return $this;
2793
	}
2794
2795
2796
	/**
2797
	 * @return bool
2798
	 */
2799
	public function hasAutoSubmit()
2800
	{
2801
		return $this->auto_submit;
2802
	}
2803
2804
2805
	/**
2806
	 * Submit button when no auto-submitting is used
2807
	 * @return Filter\SubmitButton
2808
	 */
2809
	public function getFilterSubmitButton()
2810
	{
2811
		if ($this->hasAutoSubmit()) {
2812
			throw new DataGridException(
2813
				'DataGrid has auto-submit. Turn it off before setting filter submit button.'
2814
			);
2815
		}
2816
2817
		if ($this->filter_submit_button === NULL) {
2818
			$this->filter_submit_button = new Filter\SubmitButton($this);
2819
		}
2820
2821
		return $this->filter_submit_button;
2822
	}
2823
2824
2825
	/********************************************************************************
2826
	 *                                   INTERNAL                                   *
2827
	 ********************************************************************************/
2828
2829
2830
	/**
2831
	 * Get count of columns
2832
	 * @return int
2833
	 */
2834
	public function getColumnsCount()
2835
	{
2836
		$count = sizeof($this->getColumns());
2837
2838
		if (!empty($this->actions)
2839
			|| $this->isSortable()
2840
			|| $this->getItemsDetail()
2841
			|| $this->getInlineEdit()
2842
			|| $this->getInlineAdd()) {
2843
			$count++;
2844
		}
2845
2846
		if ($this->hasGroupActions()) {
2847
			$count++;
2848
		}
2849
2850
		return $count;
2851
	}
2852
2853
2854
	/**
2855
	 * Get primary key of datagrid data source
2856
	 * @return string
2857
	 */
2858
	public function getPrimaryKey()
2859
	{
2860
		return $this->primary_key;
2861
	}
2862
2863
2864
	/**
2865
	 * Get set of set columns
2866
	 * @return Column\IColumn[]
2867
	 */
2868
	public function getColumns()
2869
	{
2870
        $columnManipulated = !$this->getSessionData('_grid_hidden_columns_manipulated', FALSE);
2871
		if ($columnManipulated)
2872
			$columns_to_hide = [];
2873
		
2874
		foreach ($this->columns as $key => $column) {
2875
            if ($columnManipulated) 
2876
				if ($column->getDefaultHide()) 
2877
					$columns_to_hide[] = $key;
0 ignored issues
show
Bug introduced by
The variable $columns_to_hide does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2878
2879
			$this->columns_visibility[$key] = [
2880
				'visible' => TRUE,
2881
				'name' => $column->getName(),
2882
				'escape' => $column->isHeaderEscaped()
2883
			];
2884
		}
2885
2886
		if ($columnManipulated and !empty($columns_to_hide)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
2887
			$this->saveSessionData('_grid_hidden_columns', $columns_to_hide);
2888
			$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2889
		}
2890
2891
		$hidden_columns = $this->getSessionData('_grid_hidden_columns', []);
2892
		
2893
		foreach ($hidden_columns as $column) {
2894
			if (!empty($this->columns[$column])) {
2895
				$this->columns_visibility[$column] = [
2896
					'visible' => FALSE,
2897
					'name' => $this->columns[$column]->getName(),
2898
					'escape' => $this->columns[$column]->isHeaderEscaped()
2899
				];
2900
				$this->removeColumn($column);
2901
			}
2902
		}
2903
2904
		return $this->columns;
2905
	}
2906
2907
2908
	/**
2909
	 * @return PresenterComponent
2910
	 */
2911
	public function getParent()
2912
	{
2913
		$parent = parent::getParent();
2914
2915
		if (!($parent instanceof PresenterComponent)) {
2916
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2917
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2918
			);
2919
		}
2920
2921
		return $parent;
2922
	}
2923
2924
2925
	/**
2926
	 * Some of datagrid columns is hidden by default
2927
	 * @param bool $default_hide
2928
	 */
2929
	public function setSomeColumnDefaultHide($default_hide)
2930
	{
2931
		$this->some_column_default_hide = $default_hide;
2932
	}
2933
2934
2935
	/**
2936
	 * Are some of columns hidden bydefault?
2937
	 */
2938
	public function hasSomeColumnDefaultHide()
2939
	{
2940
		return $this->some_column_default_hide;
2941
	}
2942
2943
}
2944