Completed
Push — master ( 13bada...124556 )
by Pavel
03:12
created

DataGrid   F

Complexity

Total Complexity 324

Size/Duplication

Total Lines 2855
Duplicated Lines 3.99 %

Coupling/Cohesion

Components 5
Dependencies 44

Importance

Changes 51
Bugs 9 Features 15
Metric Value
wmc 324
c 51
b 9
f 15
lcom 5
cbo 44
dl 114
loc 2855
rs 0.5217

126 Methods

Rating   Name   Duplication   Size   Complexity  
A hasTreeViewChildrenCallback() 0 4 1
A treeViewChildrenCallback() 0 4 1
B __construct() 0 26 1
A attached() 0 13 3
D render() 0 101 12
A setRowCallback() 0 6 1
A setPrimaryKey() 0 10 2
A setDataSource() 0 6 1
A setTemplateFile() 0 6 1
A getTemplateFile() 0 4 2
A getOriginalTemplateFile() 0 4 1
A useHappyComponents() 0 8 2
A setDefaultSort() 0 12 2
A findDefaultSort() 0 12 3
A setSortable() 0 10 2
A setSortableHandler() 0 6 1
A isSortable() 0 4 1
A getSortableHandler() 0 4 1
A createSorting() 0 9 2
A isTreeView() 0 4 1
B setTreeView() 0 30 4
A addColumnText() 0 7 2
A addColumnLink() 0 12 4
A addColumnNumber() 0 7 2
A addColumnDateTime() 0 7 2
A addColumnStatus() 0 7 2
A addColumn() 0 11 1
A getColumn() 0 8 2
A removeColumn() 0 4 1
A addColumnCheck() 0 6 2
A addAction() 0 11 3
A addActionCallback() 0 17 3
A getAction() 0 8 2
A removeAction() 0 4 1
A addActionCheck() 0 6 2
A addFilterText() 0 12 4
A addFilterSelect() 12 12 3
A addFilterMultiSelect() 0 12 3
A addFilterDate() 0 12 3
A addFilterRange() 12 12 3
A addFilterDateRange() 12 12 3
A addFilterCheck() 0 6 2
D assableFilters() 0 28 10
A removeFilter() 0 4 1
A getFilter() 0 8 2
A isFilterActive() 0 6 2
A setFilterActive() 0 6 1
A setFilter() 0 8 1
D setDefaultFilter() 0 38 9
B findDefaultFilter() 0 18 5
C createComponentFilter() 15 80 7
D filterSucceeded() 0 104 25
A setOuterFilterRendering() 0 6 1
A hasOuterFilterRendering() 0 4 1
C findSessionValues() 0 50 17
A addExportCallback() 0 8 2
A addExportCsv() 12 12 1
A addExportCsvFiltered() 12 12 1
A addToExports() 0 8 2
A resetExportsLinks() 0 6 2
A addToolbarButton() 0 6 1
A addGroupAction() 0 4 1
A addGroupSelectAction() 0 4 1
A addGroupTextAction() 0 4 1
A getGroupActionCollection() 0 8 2
A hasGroupActions() 0 4 1
A handlePage() 0 10 1
B handleSort() 0 39 5
A handleResetFilter() 0 21 4
C handleExport() 0 48 8
A handleGetChildren() 0 17 2
A handleGetItemDetail() 0 14 2
A handleEdit() 0 7 1
A reload() 0 23 3
A handleChangeStatus() 0 8 2
A redrawItem() 0 12 2
A handleShowAllColumns() 0 9 1
A handleShowDefaultColumns() 0 9 1
A handleShowColumn() 19 19 3
A handleHideColumn() 20 20 3
A handleActionCallback() 0 10 2
A setItemsPerPageList() 0 10 2
A setDefaultPerPage() 0 5 1
A findDefaultPerPage() 0 12 3
A createComponentPaginator() 0 16 1
A getPerPage() 0 12 4
A getItemsPerPageList() 0 18 4
A setPagination() 0 6 1
A isPaginated() 0 4 1
A getPaginator() 0 8 3
A setTranslator() 0 6 1
A getTranslator() 0 8 2
A setColumnsOrder() 0 18 4
A setColumnsExportOrder() 0 4 1
A getSessionSectionName() 0 4 1
A setRememberState() 0 6 1
A setRefreshUrl() 0 7 1
B getSessionData() 0 8 5
A saveSessionData() 0 6 2
A deleteSesssionData() 0 4 1
A getItemsDetail() 0 4 1
B setItemsDetail() 0 39 6
A setItemsDetailForm() 0 12 2
A getItemDetailForm() 0 8 2
A allowRowsGroupAction() 0 4 1
A allowRowsInlineEdit() 0 4 1
A allowRowsAction() 0 4 1
A getRowCondition() 0 14 4
A addColumnCallback() 0 4 1
A getColumnCallback() 0 4 2
A addInlineEdit() 0 6 2
A getInlineEdit() 0 4 1
A handleInlineEdit() 0 17 3
A addInlineAdd() 0 10 1
A getInlineAdd() 0 4 1
A canHideColumns() 0 4 1
A setColumnsHideable() 0 6 1
A hasColumnsSummary() 0 4 1
A setColumnsSummary() 0 6 1
A getColumnsSummary() 0 4 1
B getColumnsCount() 0 18 7
A getPrimaryKey() 0 4 1
C getColumns() 0 32 7
A getParent() 0 12 2
A setSomeColumnDefaultHide() 0 4 1
A hasSomeColumnDefaultHide() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DataGrid often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DataGrid, and based on these observations, apply Extract Interface, too.

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