Completed
Push — master ( ff0565...1f93e9 )
by Pavel
7s
created

DataGrid::getParent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2015 ublaboo <[email protected]>
5
 * @author      Pavel Janda <[email protected]>
6
 * @package     Ublaboo
7
 */
8
9
namespace Ublaboo\DataGrid;
10
11
use Nette;
12
use Nette\Application\UI\PresenterComponent;
13
use Ublaboo\DataGrid\Utils\ArraysHelper;
14
use Nette\Application\UI\Form;
15
use Ublaboo\DataGrid\Exception\DataGridException;
16
use Ublaboo\DataGrid\Exception\DataGridHasToBeAttachedToPresenterComponentException;
17
use Ublaboo\DataGrid\Utils\Sorting;
18
use Ublaboo\DataGrid\InlineEdit\InlineEdit;
19
20
/**
21
 * @method onRedraw()
22
 * @method onRender()
23
 * @method onColumnAdd()
24
 */
25
class DataGrid extends Nette\Application\UI\Control
26
{
27
28
	/**
29
	 * @var callable[]
30
	 */
31
	public $onRedraw;
32
33
	/**
34
	 * @var callable[]
35
	 */
36
	public $onRender = [];
37
38
	/**
39
	 * @var callable[]
40
	 */
41
	public $onColumnAdd;
42
43
	/**
44
	 * @var string
45
	 */
46
	public static $icon_prefix = 'fa fa-';
47
48
	/**
49
	 * When set to TRUE, datagrid throws an exception
50
	 * 	when tring to get related entity within join and entity does not exist
51
	 * @var bool
52
	 */
53
	public $strict_entity_property = FALSE;
54
55
	/**
56
	 * @var int
57
	 * @persistent
58
	 */
59
	public $page = 1;
60
61
	/**
62
	 * @var int|string
63
	 * @persistent
64
	 */
65
	public $per_page;
66
67
	/**
68
	 * @var array
69
	 * @persistent
70
	 */
71
	public $sort = [];
72
73
	/**
74
	 * @var array
75
	 */
76
	public $default_sort = [];
77
78
	/**
79
	 * @var array
80
	 * @persistent
81
	 */
82
	public $filter = [];
83
84
	/**
85
	 * @var callable|null
86
	 */
87
	protected $sort_callback = NULL;
88
89
	/**
90
	 * @var bool
91
	 */
92
	protected $use_happy_components = TRUE;
93
94
	/**
95
	 * @var callable
96
	 */
97
	protected $rowCallback;
98
99
	/**
100
	 * @var array
101
	 */
102
	protected $items_per_page_list;
103
104
	/**
105
	 * @var string
106
	 */
107
	protected $template_file;
108
109
	/**
110
	 * @var Column\IColumn[]
111
	 */
112
	protected $columns = [];
113
114
	/**
115
	 * @var Column\Action[]
116
	 */
117
	protected $actions = [];
118
119
	/**
120
	 * @var GroupAction\GroupActionCollection
121
	 */
122
	protected $group_action_collection;
123
124
	/**
125
	 * @var Filter\Filter[]
126
	 */
127
	protected $filters = [];
128
129
	/**
130
	 * @var Export\Export[]
131
	 */
132
	protected $exports = [];
133
134
	/**
135
	 * @var DataModel
136
	 */
137
	protected $dataModel;
138
139
	/**
140
	 * @var DataFilter
141
	 */
142
	protected $dataFilter;
143
144
	/**
145
	 * @var string
146
	 */
147
	protected $primary_key = 'id';
148
149
	/**
150
	 * @var bool
151
	 */
152
	protected $do_paginate = TRUE;
153
154
	/**
155
	 * @var bool
156
	 */
157
	protected $csv_export = TRUE;
158
159
	/**
160
	 * @var bool
161
	 */
162
	protected $csv_export_filtered = TRUE;
163
164
	/**
165
	 * @var bool
166
	 */
167
	protected $sortable = FALSE;
168
169
	/**
170
	 * @var string
171
	 */
172
	protected $sortable_handler = 'sort!';
173
174
	/**
175
	 * @var string
176
	 */
177
	protected $original_template;
178
179
	/**
180
	 * @var array
181
	 */
182
	protected $redraw_item;
183
184
	/**
185
	 * @var mixed
186
	 */
187
	protected $translator;
188
189
	/**
190
	 * @var bool
191
	 */
192
	protected $force_filter_active;
193
194
	/**
195
	 * @var callable
196
	 */
197
	protected $tree_view_children_callback;
198
199
	/**
200
	 * @var callable
201
	 */
202
	protected $tree_view_has_children_callback;
203
204
	/**
205
	 * @var string
206
	 */
207
	protected $tree_view_has_children_column;
208
209
	/**
210
	 * @var bool
211
	 */
212
	protected $outer_filter_rendering = FALSE;
213
214
	/**
215
	 * @var array
216
	 */
217
	protected $columns_export_order = [];
218
219
	/**
220
	 * @var bool
221
	 */
222
	protected $remember_state = TRUE;
223
224
	/**
225
	 * @var bool
226
	 */
227
	protected $refresh_url = TRUE;
228
229
	/**
230
	 * @var Nette\Http\SessionSection
231
	 */
232
	protected $grid_session;
233
234
	/**
235
	 * @var Column\ItemDetail
236
	 */
237
	protected $items_detail;
238
239
	/**
240
	 * @var array
241
	 */
242
	protected $row_conditions = [
243
		'group_action' => FALSE,
244
		'action' => []
245
	];
246
247
	/**
248
	 * @var array
249
	 */
250
	protected $column_callbacks = [];
251
252
	/**
253
	 * @var bool
254
	 */
255
	protected $can_hide_columns = FALSE;
256
257
	/**
258
	 * @var array
259
	 */
260
	protected $columns_visibility = [];
261
262
	/**
263
	 * @var InlineEdit
264
	 */
265
	protected $inlineEdit;
266
267
	/**
268
	 * @var InlineEdit
269
	 */
270
	protected $inlineAdd;
271
272
	/**
273
	 * @var bool
274
	 */
275
	protected $snippets_set = FALSE;
276
277
278
	/**
279
	 * @param Nette\ComponentModel\IContainer|NULL $parent
280
	 * @param string                               $name
281
	 */
282
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
283
	{
284
		parent::__construct($parent, $name);
285
286
		$this->monitor('Nette\Application\UI\Presenter');
287
288
		/**
289
		 * Try to find previous filters/pagination/sort in session
290
		 */
291
		$this->onRender[] = [$this, 'findSessionFilters'];
292
		$this->onRender[] = [$this, 'findDefaultSort'];
293
	}
294
295
296
	/**
297
	 * {inheritDoc}
298
	 * @return void
299
	 */
300
	public function attached($presenter)
301
	{
302
		parent::attached($presenter);
303
304
		if ($presenter instanceof Nette\Application\UI\Presenter) {
305
			/**
306
			 * Get session
307
			 */
308
			if ($this->remember_state) {
309
				$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...
310
			}
311
		}
312
	}
313
314
315
	/********************************************************************************
316
	 *                                  RENDERING                                   *
317
	 ********************************************************************************/
318
319
320
	/**
321
	 * Render template
322
	 * @return void
323
	 */
324
	public function render()
325
	{
326
		/**
327
		 * Check whether datagrid has set some columns, initiated data source, etc
328
		 */
329
		if (!($this->dataModel instanceof DataModel)) {
330
			throw new DataGridException('You have to set a data source first.');
331
		}
332
333
		if (empty($this->columns)) {
334
			throw new DataGridException('You have to add at least one column.');
335
		}
336
337
		$this->template->setTranslator($this->getTranslator());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
338
339
		/**
340
		 * Invoke possible events
341
		 */
342
		$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...
343
344
		/**
345
		 * Prepare data for rendering (datagrid may render just one item)
346
		 */
347
		$rows = [];
348
349
		if (!empty($this->redraw_item)) {
350
			$items = $this->dataModel->filterRow($this->redraw_item);
351
		} else {
352
			$items = Nette\Utils\Callback::invokeArgs(
353
				[$this->dataModel, 'filterData'],
354
				[
355
					$this->getPaginator(),
356
					new Sorting($this->sort, $this->sort_callback),
357
					$this->assableFilters()
358
				]
359
			);
360
		}
361
362
		$callback = $this->rowCallback ?: NULL;
363
364
		foreach ($items as $item) {
365
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
366
367
			if ($callback) {
368
				$callback($item, $row->getControl());
369
			}
370
		}
371
372
		if ($this->isTreeView()) {
373
			$this->template->add('tree_view_has_children_column', $this->tree_view_has_children_column);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
374
		}
375
376
		$this->template->add('rows', $rows);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
377
378
		$this->template->add('columns', $this->getColumns());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
379
		$this->template->add('actions', $this->actions);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
380
		$this->template->add('exports', $this->exports);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
381
		$this->template->add('filters', $this->filters);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
382
383
		$this->template->add('filter_active', $this->isFilterActive());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
384
		$this->template->add('original_template', $this->getOriginalTemplateFile());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
385
		$this->template->add('icon_prefix', static::$icon_prefix);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
386
		$this->template->add('items_detail', $this->items_detail);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
387
		$this->template->add('columns_visibility', $this->columns_visibility);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
388
389
		$this->template->add('inlineEdit', $this->inlineEdit);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
390
		$this->template->add('inlineAdd', $this->inlineAdd);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
391
392
		/**
393
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
394
		 */
395
		$this->template->add('filter', $this['filter']);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
396
397
		/**
398
		 * Set template file and render it
399
		 */
400
		$this->template->setFile($this->getTemplateFile());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
401
		$this->template->render();
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
402
	}
403
404
405
	/********************************************************************************
406
	 *                                 ROW CALLBACK                                 *
407
	 ********************************************************************************/
408
409
410
	/**
411
	 * Each row can be modified with user callback
412
	 * @param  callable  $callback
413
	 * @return static
414
	 */
415
	public function setRowCallback(callable $callback)
416
	{
417
		$this->rowCallback = $callback;
418
419
		return $this;
420
	}
421
422
423
	/********************************************************************************
424
	 *                                 DATA SOURCE                                  *
425
	 ********************************************************************************/
426
427
428
	/**
429
	 * By default ID, you can change that
430
	 * @param string $primary_key
431
	 * @return static
432
	 */
433
	public function setPrimaryKey($primary_key)
434
	{
435
		if ($this->dataModel instanceof DataModel) {
436
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
437
		}
438
439
		$this->primary_key = $primary_key;
440
441
		return $this;
442
	}
443
444
445
	/**
446
	 * Set Grid data source
447
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
448
	 * @return static
449
	 */
450
	public function setDataSource($source)
451
	{
452
		$this->dataModel = new DataModel($source, $this->primary_key);
453
454
		return $this;
455
	}
456
457
458
	/********************************************************************************
459
	 *                                  TEMPLATING                                  *
460
	 ********************************************************************************/
461
462
463
	/**
464
	 * Set custom template file to render
465
	 * @param string $template_file
466
	 * @return static
467
	 */
468
	public function setTemplateFile($template_file)
469
	{
470
		$this->template_file = $template_file;
471
472
		return $this;
473
	}
474
475
476
	/**
477
	 * Get DataGrid template file
478
	 * @return string
479
	 * @return static
480
	 */
481
	public function getTemplateFile()
482
	{
483
		return $this->template_file ?: $this->getOriginalTemplateFile();
484
	}
485
486
487
	/**
488
	 * Get DataGrid original template file
489
	 * @return string
490
	 */
491
	public function getOriginalTemplateFile()
492
	{
493
		return __DIR__.'/templates/datagrid.latte';
494
	}
495
496
497
	/**
498
	 * Tell datagrid wheteher to use or not happy components
499
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
500
	 * @return void|bool
501
	 */
502
	public function useHappyComponents($use = NULL)
503
	{
504
		if (NULL === $use) {
505
			return $this->use_happy_components;
506
		}
507
508
		$this->use_happy_components = (bool) $use;
509
	}
510
511
512
	/********************************************************************************
513
	 *                                   SORTING                                    *
514
	 ********************************************************************************/
515
516
517
	/**
518
	 * Set default sorting
519
	 * @param array $sort
520
	 * @return static
521
	 */
522
	public function setDefaultSort($sort)
523
	{
524
		if (is_string($sort)) {
525
			$sort = [$sort => 'ASC'];
526
		} else {
527
			$sort = (array) $sort;
528
		}
529
530
		$this->default_sort = $sort;
531
532
		return $this;
533
	}
534
535
536
	/**
537
	 * User may set default sorting, apply it
538
	 * @return void
539
	 */
540
	public function findDefaultSort()
541
	{
542
		if (!empty($this->sort)) {
543
			return;
544
		}
545
546
		if (!empty($this->default_sort)) {
547
			$this->sort = $this->default_sort;
548
		}
549
550
		$this->saveSessionData('_grid_sort', $this->sort);
551
	}
552
553
554
	/**
555
	 * Set grido to be sortable
556
	 * @param bool $sortable
557
	 * @return static
558
	 */
559
	public function setSortable($sortable = TRUE)
560
	{
561
		if ($this->getItemsDetail()) {
562
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
563
		}
564
565
		$this->sortable = (bool) $sortable;
566
567
		return $this;
568
	}
569
570
571
	/**
572
	 * Set sortable handle
573
	 * @param string $handler
574
	 * @return static
575
	 */
576
	public function setSortableHandler($handler = 'sort!')
577
	{
578
		$this->sortable_handler = (string) $handler;
579
580
		return $this;
581
	}
582
583
584
	/**
585
	 * Tell whether DataGrid is sortable
586
	 * @return bool
587
	 */
588
	public function isSortable()
589
	{
590
		return $this->sortable;
591
	}
592
593
	/**
594
	 * Return sortable handle name
595
	 * @return string
596
	 */
597
	public function getSortableHandler()
598
	{
599
		return $this->sortable_handler;
600
	}
601
602
603
	/********************************************************************************
604
	 *                                  TREE VIEW                                   *
605
	 ********************************************************************************/
606
607
608
	/**
609
	 * Is tree view set?
610
	 * @return boolean
611
	 */
612
	public function isTreeView()
613
	{
614
		return (bool) $this->tree_view_children_callback;
615
	}
616
617
618
	/**
619
	 * Setting tree view
620
	 * @param callable $get_children_callback
621
	 * @param string|callable $tree_view_has_children_column
622
	 * @return static
623
	 */
624
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
625
	{
626
		if (!is_callable($get_children_callback)) {
627
			throw new DataGridException(
628
				'Parameters to method DataGrid::setTreeView must be of type callable'
629
			);
630
		}
631
632
		if (is_callable($tree_view_has_children_column)) {
633
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
634
			$tree_view_has_children_column = NULL;
635
		}
636
637
		$this->tree_view_children_callback = $get_children_callback;
638
		$this->tree_view_has_children_column = $tree_view_has_children_column;
639
640
		/**
641
		 * TUrn off pagination
642
		 */
643
		$this->setPagination(FALSE);
644
645
		/**
646
		 * Set tree view template file
647
		 */
648
		if (!$this->template_file) {
649
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
650
		}
651
652
		return $this;
653
	}
654
655
656
	/**
657
	 * Is tree view children callback set?
658
	 * @return boolean
659
	 */
660
	public function hasTreeViewChildrenCallback()
661
	{
662
		return is_callable($this->tree_view_has_children_callback);
663
	}
664
665
666
	/**
667
	 * @param  mixed $item
668
	 * @return boolean
669
	 */
670
	public function treeViewChildrenCallback($item)
671
	{
672
		return call_user_func($this->tree_view_has_children_callback, $item);
673
	}
674
675
676
	/********************************************************************************
677
	 *                                    COLUMNS                                   *
678
	 ********************************************************************************/
679
680
681
	/**
682
	 * Add text column with no other formating
683
	 * @param  string      $key
684
	 * @param  string      $name
685
	 * @param  string|null $column
686
	 * @return Column\ColumnText
687
	 */
688
	public function addColumnText($key, $name, $column = NULL)
689
	{
690
		$this->addColumnCheck($key);
691
		$column = $column ?: $key;
692
693
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
694
	}
695
696
697
	/**
698
	 * Add column with link
699
	 * @param  string      $key
700
	 * @param  string      $name
701
	 * @param  string|null $column
702
	 * @return Column\ColumnLink
703
	 */
704
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
705
	{
706
		$this->addColumnCheck($key);
707
		$column = $column ?: $key;
708
		$href = $href ?: $key;
709
710
		if (NULL === $params) {
711
			$params = [$this->primary_key];
712
		}
713
714
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
715
	}
716
717
718
	/**
719
	 * Add column with possible number formating
720
	 * @param  string      $key
721
	 * @param  string      $name
722
	 * @param  string|null $column
723
	 * @return Column\ColumnNumber
724
	 */
725
	public function addColumnNumber($key, $name, $column = NULL)
726
	{
727
		$this->addColumnCheck($key);
728
		$column = $column ?: $key;
729
730
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
731
	}
732
733
734
	/**
735
	 * Add column with date formating
736
	 * @param  string      $key
737
	 * @param  string      $name
738
	 * @param  string|null $column
739
	 * @return Column\ColumnDateTime
740
	 */
741
	public function addColumnDateTime($key, $name, $column = NULL)
742
	{
743
		$this->addColumnCheck($key);
744
		$column = $column ?: $key;
745
746
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
747
	}
748
749
750
	/**
751
	 * Add column status
752
	 * @param  string      $key
753
	 * @param  string      $name
754
	 * @param  string|null $column
755
	 * @return Column\ColumnStatus
756
	 */
757
	public function addColumnStatus($key, $name, $column = NULL)
758
	{
759
		$this->addColumnCheck($key);
760
		$column = $column ?: $key;
761
762
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
763
	}
764
765
766
	/**
767
	 * @param string $key
768
	 * @param Column\Column $column
769
	 * @return Column\Column
770
	 */
771
	protected function addColumn($key, Column\Column $column)
772
	{
773
		$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...
774
775
		$this->columns_visibility[$key] = [
776
			'visible' => TRUE,
777
			'name' => $column->getName()
778
		];
779
780
		return $this->columns[$key] = $column;
781
	}
782
783
784
	/**
785
	 * Return existing column
786
	 * @param  string $key
787
	 * @return Column\Column
788
	 * @throws DataGridException
789
	 */
790
	public function getColumn($key)
791
	{
792
		if (!isset($this->columns[$key])) {
793
			throw new DataGridException("There is no column at key [$key] defined.");
794
		}
795
796
		return $this->columns[$key];
797
	}
798
799
800
	/**
801
	 * Remove column
802
	 * @param string $key
803
	 * @return void
804
	 */
805
	public function removeColumn($key)
806
	{
807
		unset($this->columns[$key]);
808
	}
809
810
811
	/**
812
	 * Check whether given key already exists in $this->columns
813
	 * @param  string $key
814
	 * @throws DataGridException
815
	 */
816
	protected function addColumnCheck($key)
817
	{
818
		if (isset($this->columns[$key])) {
819
			throw new DataGridException("There is already column at key [$key] defined.");
820
		}
821
	}
822
823
824
	/********************************************************************************
825
	 *                                    ACTIONS                                   *
826
	 ********************************************************************************/
827
828
829
	/**
830
	 * Create action
831
	 * @param string     $key
832
	 * @param string     $name
833
	 * @param string     $href
834
	 * @param array|null $params
835
	 * @return Column\Action
836
	 */
837
	public function addAction($key, $name, $href = NULL, array $params = NULL)
838
	{
839
		$this->addActionCheck($key);
840
		$href = $href ?: $key;
841
842
		if (NULL === $params) {
843
			$params = [$this->primary_key];
844
		}
845
846
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
847
	}
848
849
850
	/**
851
	 * Create action callback
852
	 * @param string     $key
853
	 * @param string     $name
854
	 * @return Column\Action
855
	 */
856
	public function addActionCallback($key, $name, $callback = NULL)
857
	{
858
		$this->addActionCheck($key);
859
		$params = ['__id' => $this->primary_key];
860
861
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
862
863
		if ($callback) {
864
			if (!is_callable($callback)) {
865
				throw new DataGridException('ActionCallback callback has to be callable.');
866
			}
867
868
			$action->onClick[] = $callback;
869
		}
870
871
		return $action;
872
	}
873
874
875
	/**
876
	 * Get existing action
877
	 * @param  string       $key
878
	 * @return Column\Action
879
	 * @throws DataGridException
880
	 */
881
	public function getAction($key)
882
	{
883
		if (!isset($this->actions[$key])) {
884
			throw new DataGridException("There is no action at key [$key] defined.");
885
		}
886
887
		return $this->actions[$key];
888
	}
889
890
891
	/**
892
	 * Remove action
893
	 * @param string $key
894
	 * @return void
895
	 */
896
	public function removeAction($key)
897
	{
898
		unset($this->actions[$key]);
899
	}
900
901
902
	/**
903
	 * Check whether given key already exists in $this->filters
904
	 * @param  string $key
905
	 * @throws DataGridException
906
	 */
907
	protected function addActionCheck($key)
908
	{
909
		if (isset($this->actions[$key])) {
910
			throw new DataGridException("There is already action at key [$key] defined.");
911
		}
912
	}
913
914
915
	/********************************************************************************
916
	 *                                    FILTERS                                   *
917
	 ********************************************************************************/
918
919
920
	/**
921
	 * Add filter fot text search
922
	 * @param string       $key
923
	 * @param string       $name
924
	 * @param array|string $columns
925
	 * @return Filter\FilterText
926
	 * @throws DataGridException
927
	 */
928
	public function addFilterText($key, $name, $columns = NULL)
929
	{
930
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
931
932
		if (!is_array($columns)) {
933
			throw new DataGridException("Filter Text can except only array or string.");
934
		}
935
936
		$this->addFilterCheck($key);
937
938
		return $this->filters[$key] = new Filter\FilterText($key, $name, $columns);
939
	}
940
941
942
	/**
943
	 * Add select box filter
944
	 * @param string $key
945
	 * @param string $name
946
	 * @param array  $options
947
	 * @param string $column
948
	 * @return Filter\FilterSelect
949
	 * @throws DataGridException
950
	 */
951 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...
952
	{
953
		$column = $column ?: $key;
954
955
		if (!is_string($column)) {
956
			throw new DataGridException("Filter Select can only filter in one column.");
957
		}
958
959
		$this->addFilterCheck($key);
960
961
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
962
	}
963
964
965
	/**
966
	 * Add multi select box filter
967
	 * @param string $key
968
	 * @param string $name
969
	 * @param array  $options
970
	 * @param string $column
971
	 * @return Filter\FilterSelect
972
	 * @throws DataGridException
973
	 */
974
	public function addFilterMultiSelect($key, $name, array $options, $column = NULL)
975
	{
976
		$column = $column ?: $key;
977
978
		if (!is_string($column)) {
979
			throw new DataGridException("Filter MultiSelect can only filter in one column.");
980
		}
981
982
		$this->addFilterCheck($key);
983
984
		return $this->filters[$key] = new Filter\FilterMultiSelect($key, $name, $options, $column);
985
	}
986
987
988
	/**
989
	 * Add datepicker filter
990
	 * @param string $key
991
	 * @param string $name
992
	 * @param string $column
993
	 * @return Filter\FilterDate
994
	 * @throws DataGridException
995
	 */
996
	public function addFilterDate($key, $name, $column = NULL)
997
	{
998
		$column = $column ?: $key;
999
1000
		if (!is_string($column)) {
1001
			throw new DataGridException("FilterDate can only filter in one column.");
1002
		}
1003
1004
		$this->addFilterCheck($key);
1005
1006
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
1007
	}
1008
1009
1010
	/**
1011
	 * Add range filter (from - to)
1012
	 * @param string $key
1013
	 * @param string $name
1014
	 * @param string $column
1015
	 * @return Filter\FilterRange
1016
	 * @throws DataGridException
1017
	 */
1018 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...
1019
	{
1020
		$column = $column ?: $key;
1021
1022
		if (!is_string($column)) {
1023
			throw new DataGridException("FilterRange can only filter in one column.");
1024
		}
1025
1026
		$this->addFilterCheck($key);
1027
1028
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
1029
	}
1030
1031
1032
	/**
1033
	 * Add datepicker filter (from - to)
1034
	 * @param string $key
1035
	 * @param string $name
1036
	 * @param string $column
1037
	 * @return Filter\FilterDateRange
1038
	 * @throws DataGridException
1039
	 */
1040 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...
1041
	{
1042
		$column = $column ?: $key;
1043
1044
		if (!is_string($column)) {
1045
			throw new DataGridException("FilterDateRange can only filter in one column.");
1046
		}
1047
1048
		$this->addFilterCheck($key);
1049
1050
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
1051
	}
1052
1053
1054
	/**
1055
	 * Check whether given key already exists in $this->filters
1056
	 * @param  string $key
1057
	 * @throws DataGridException
1058
	 */
1059
	protected function addFilterCheck($key)
1060
	{
1061
		if (isset($this->filters[$key])) {
1062
			throw new DataGridException("There is already action at key [$key] defined.");
1063
		}
1064
	}
1065
1066
1067
	/**
1068
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1069
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1070
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1071
	 */
1072
	public function assableFilters()
1073
	{
1074
		foreach ($this->filter as $key => $value) {
1075
			if (!isset($this->filters[$key])) {
1076
				$this->deleteSesssionData($key);
1077
1078
				continue;
1079
			}
1080
1081
			if (is_array($value) || $value instanceof \Traversable) {
1082
				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...
1083
					$this->filters[$key]->setValue($value);
1084
				}
1085
			} else {
1086
				if ($value !== '' && $value !== NULL) {
1087
					$this->filters[$key]->setValue($value);
1088
				}
1089
			}
1090
		}
1091
1092
		foreach ($this->columns as $column) {
1093
			if (isset($this->sort[$column->getSortingColumn()])) {
1094
				$column->setSort($this->sort);
1095
			}
1096
		}
1097
1098
		return $this->filters;
1099
	}
1100
1101
1102
	/**
1103
	 * Remove filter
1104
	 * @param string $key
1105
	 * @return void
1106
	 */
1107
	public function removeFilter($key)
1108
	{
1109
		unset($this->filters[$key]);
1110
	}
1111
1112
1113
	/**
1114
	 * Get defined filter
1115
	 * @param  string $key
1116
	 * @return Filter\Filter
1117
	 */
1118
	public function getFilter($key)
1119
	{
1120
		if (!isset($this->filters[$key])) {
1121
			throw new DataGridException("Filter [{$key}] is not defined");
1122
		}
1123
1124
		return $this->filters[$key];
1125
	}
1126
1127
1128
	/********************************************************************************
1129
	 *                                  FILTERING                                   *
1130
	 ********************************************************************************/
1131
1132
1133
	/**
1134
	 * Is filter active?
1135
	 * @return boolean
1136
	 */
1137
	public function isFilterActive()
1138
	{
1139
		$is_filter = ArraysHelper::testTruthy($this->filter);
1140
1141
		return ($is_filter) || $this->force_filter_active;
1142
	}
1143
1144
1145
	/**
1146
	 * Tell that filter is active from whatever reasons
1147
	 * return static
1148
	 */
1149
	public function setFilterActive()
1150
	{
1151
		$this->force_filter_active = TRUE;
1152
1153
		return $this;
1154
	}
1155
1156
1157
	/**
1158
	 * If we want to sent some initial filter
1159
	 * @param array $filter
1160
	 * @return static
1161
	 */
1162
	public function setFilter(array $filter)
1163
	{
1164
		$this->filter = $filter;
1165
1166
		return $this;
1167
	}
1168
1169
1170
	/**
1171
	 * FilterAndGroupAction form factory
1172
	 * @return Form
1173
	 */
1174
	public function createComponentFilter()
1175
	{
1176
		$form = new Form($this, 'filter');
1177
1178
		$form->setMethod('get');
1179
1180
		$form->setTranslator($this->getTranslator());
1181
1182
		/**
1183
		 * InlineEdit part
1184
		 */
1185
		$inline_edit_container = $form->addContainer('inline_edit');
1186
1187 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...
1188
			$inline_edit_container->addSubmit('submit', 'ublaboo_datagrid.save');
1189
			$inline_edit_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1190
				->setValidationScope(FALSE);
1191
1192
			$this->inlineEdit->onControlAdd($inline_edit_container);
1193
		}
1194
1195
		/**
1196
		 * InlineAdd part
1197
		 */
1198
		$inline_add_container = $form->addContainer('inline_add');
1199
1200 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...
1201
			$inline_add_container->addSubmit('submit', 'ublaboo_datagrid.save');
1202
			$inline_add_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1203
				->setValidationScope(FALSE)
1204
				->setAttribute('data-datagrid-cancel-inline-add', TRUE);
1205
1206
			$this->inlineAdd->onControlAdd($inline_add_container);
1207
		}
1208
1209
		/**
1210
		 * ItemDetail form part
1211
		 */
1212
		$items_detail_form = $this->getItemDetailForm();
1213
1214
		if ($items_detail_form instanceof Nette\Forms\Container) {
1215
			$form['items_detail_form'] = $items_detail_form;
1216
		}
1217
1218
		/**
1219
		 * Filter part
1220
		 */
1221
		$filter_container = $form->addContainer('filter');
1222
1223
		foreach ($this->filters as $filter) {
1224
			$filter->addToFormContainer($filter_container);
1225
		}
1226
1227
		/**
1228
		 * Group action part
1229
		 */
1230
		$group_action_container = $form->addContainer('group_action');
1231
1232
		if ($this->hasGroupActions()) {
1233
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1234
		}
1235
1236
		$form->setDefaults(['filter' => $this->filter]);
1237
1238
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1239
1240
		return $form;
1241
	}
1242
1243
1244
	/**
1245
	 * Set $this->filter values after filter form submitted
1246
	 * @param  Form $form
1247
	 * @return void
1248
	 */
1249
	public function filterSucceeded(Form $form)
1250
	{
1251
		if ($this->snippets_set) {
1252
			return;
1253
		}
1254
1255
		$values = $form->getValues();
1256
1257
		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...
1258
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1259
				return;
1260
			}
1261
		}
1262
1263
		/**
1264
		 * Inline edit
1265
		 */
1266
		$inline_edit = $form['inline_edit'];
1267
1268
		if (isset($inline_edit) && isset($inline_edit['submit']) && isset($inline_edit['cancel'])) {
1269
			if ($inline_edit['submit']->isSubmittedBy() || $inline_edit['cancel']->isSubmittedBy()) {
1270
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1271
				$primary_where_column = $form->getHttpData(
1272
					Form::DATA_LINE,
1273
					'inline_edit[_primary_where_column]'
1274
				);
1275
1276 View Code Duplication
				if ($inline_edit['submit']->isSubmittedBy()) {
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...
1277
					$this->inlineEdit->onSubmit($id, $values->inline_edit);
1278
1279
					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...
1280
						$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...
1281
					}
1282
				}
1283
1284
				$this->redrawItem($id, $primary_where_column);
1285
1286
				return;
1287
			}
1288
		}
1289
1290
		/**
1291
		 * Inline add
1292
		 */
1293
		$inline_add = $form['inline_add'];
1294
1295
		if (isset($inline_add) && isset($inline_add['submit']) && isset($inline_add['cancel'])) {
1296
			if ($inline_add['submit']->isSubmittedBy() || $inline_add['cancel']->isSubmittedBy()) {
1297 View Code Duplication
				if ($inline_add['submit']->isSubmittedBy()) {
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...
1298
					$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...
1299
1300
					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...
1301
						$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...
1302
					}
1303
				}
1304
1305
				return;
1306
			}
1307
		}
1308
1309
		/**
1310
		 * Filter itself
1311
		 */
1312
		$values = $values['filter'];
1313
1314
		foreach ($values as $key => $value) {
1315
			/**
1316
			 * Session stuff
1317
			 */
1318
			$this->saveSessionData($key, $value);
1319
1320
			/**
1321
			 * Other stuff
1322
			 */
1323
			$this->filter[$key] = $value;
1324
		}
1325
1326
		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...
1327
			$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...
1328
1329
			foreach ($this->columns as $key => $column) {
1330
				if ($column->isSortable()) {
1331
					$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...
1332
						'sort' => $column->getSortNext()
1333
					]);
1334
				}
1335
			}
1336
		}
1337
1338
		$this->reload();
1339
	}
1340
1341
1342
	/**
1343
	 * Should be datagrid filters rendered separately?
1344
	 * @param boolean $out
1345
	 * @return static
1346
	 */
1347
	public function setOuterFilterRendering($out = TRUE)
1348
	{
1349
		$this->outer_filter_rendering = (bool) $out;
1350
1351
		return $this;
1352
	}
1353
1354
1355
	/**
1356
	 * Are datagrid filters rendered separately?
1357
	 * @return boolean
1358
	 */
1359
	public function hasOuterFilterRendering()
1360
	{
1361
		return $this->outer_filter_rendering;
1362
	}
1363
1364
1365
	/**
1366
	 * Try to restore session stuff
1367
	 * @return void
1368
	 */
1369
	public function findSessionFilters()
1370
	{
1371
		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...
1372
			return;
1373
		}
1374
1375
		if (!$this->remember_state) {
1376
			return;
1377
		}
1378
1379
		if ($page = $this->getSessionData('_grid_page')) {
1380
			$this->page = $page;
1381
		}
1382
1383
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1384
			$this->per_page = $per_page;
1385
		}
1386
1387
		if ($sort = $this->getSessionData('_grid_sort')) {
1388
			$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...
1389
		}
1390
1391
		foreach ($this->getSessionData() as $key => $value) {
1392
			$other_session_keys = [
1393
				'_grid_per_page',
1394
				'_grid_sort',
1395
				'_grid_page',
1396
				'_grid_hidden_columns',
1397
				'_grid_hidden_columns_manipulated'
1398
			];
1399
1400
			if (!in_array($key, $other_session_keys)) {
1401
				$this->filter[$key] = $value;
1402
			}
1403
		}
1404
	}
1405
1406
1407
	/********************************************************************************
1408
	 *                                    EXPORTS                                   *
1409
	 ********************************************************************************/
1410
1411
1412
	/**
1413
	 * Add export of type callback
1414
	 * @param string $text
1415
	 * @param callable $callback
1416
	 * @param boolean $filtered
1417
	 * @return Export\Export
1418
	 */
1419
	public function addExportCallback($text, $callback, $filtered = FALSE)
1420
	{
1421
		if (!is_callable($callback)) {
1422
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1423
		}
1424
1425
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1426
	}
1427
1428
1429
	/**
1430
	 * Add already implemented csv export
1431
	 * @param string      $text
1432
	 * @param string      $csv_file_name
1433
	 * @param string|null $output_encoding
1434
	 * @param string|null $delimiter
1435
	 * @return Export\Export
1436
	 */
1437
	public function addExportCsv($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL)
1438
	{
1439
		return $this->addToExports(new Export\ExportCsv(
1440
			$text,
1441
			$csv_file_name,
1442
			FALSE,
1443
			$output_encoding,
1444
			$delimiter
1445
		));
1446
	}
1447
1448
1449
	/**
1450
	 * Add already implemented csv export, but for filtered data
1451
	 * @param string      $text
1452
	 * @param string      $csv_file_name
1453
	 * @param string|null $output_encoding
1454
	 * @param string|null $delimiter
1455
	 * @return Export\Export
1456
	 */
1457
	public function addExportCsvFiltered($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL)
1458
	{
1459
		return $this->addToExports(new Export\ExportCsv(
1460
			$text,
1461
			$csv_file_name,
1462
			TRUE,
1463
			$output_encoding,
1464
			$delimiter
1465
		));
1466
	}
1467
1468
1469
	/**
1470
	 * Add export to array
1471
	 * @param Export\Export $export
1472
	 * @return Export\Export
1473
	 */
1474
	protected function addToExports(Export\Export $export)
1475
	{
1476
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1477
1478
		$export->setLink($this->link('export!', ['id' => $id]));
1479
1480
		return $this->exports[$id] = $export;
1481
	}
1482
1483
1484
	public function resetExportsLinks()
1485
	{
1486
		foreach ($this->exports as $id => $export) {
1487
			$export->setLink($this->link('export!', ['id' => $id]));
1488
		}
1489
	}
1490
1491
1492
	/********************************************************************************
1493
	 *                                 GROUP ACTIONS                                *
1494
	 ********************************************************************************/
1495
1496
1497
	/**
1498
	 * Alias for add group select action
1499
	 * @param string $title
1500
	 * @param array  $options
1501
	 * @return GroupAction\GroupAction
1502
	 */
1503
	public function addGroupAction($title, $options = [])
1504
	{
1505
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1506
	}
1507
1508
	/**
1509
	 * Add group action (select box)
1510
	 * @param string $title
1511
	 * @param array  $options
1512
	 * @return GroupAction\GroupAction
1513
	 */
1514
	public function addGroupSelectAction($title, $options = [])
1515
	{
1516
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1517
	}
1518
1519
	/**
1520
	 * Add group action (text input)
1521
	 * @param string $title
1522
	 * @return GroupAction\GroupAction
1523
	 */
1524
	public function addGroupTextAction($title)
1525
	{
1526
		return $this->getGroupActionCollection()->addGroupTextAction($title);
1527
	}
1528
1529
	/**
1530
	 * Get collection of all group actions
1531
	 * @return GroupAction\GroupActionCollection
1532
	 */
1533
	public function getGroupActionCollection()
1534
	{
1535
		if (!$this->group_action_collection) {
1536
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1537
		}
1538
1539
		return $this->group_action_collection;
1540
	}
1541
1542
1543
	/**
1544
	 * Has datagrid some group actions?
1545
	 * @return boolean
1546
	 */
1547
	public function hasGroupActions()
1548
	{
1549
		return (bool) $this->group_action_collection;
1550
	}
1551
1552
1553
	/********************************************************************************
1554
	 *                                   HANDLERS                                   *
1555
	 ********************************************************************************/
1556
1557
1558
	/**
1559
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1560
	 * @param  int  $page
1561
	 * @return void
1562
	 */
1563
	public function handlePage($page)
1564
	{
1565
		/**
1566
		 * Session stuff
1567
		 */
1568
		$this->page = $page;
1569
		$this->saveSessionData('_grid_page', $page);
1570
1571
		$this->reload(['table']);
1572
	}
1573
1574
1575
	/**
1576
	 * Handler for sorting
1577
	 * @param array $sort
1578
	 * @return void
1579
	 */
1580
	public function handleSort(array $sort)
1581
	{
1582
		$new_sort = [];
1583
1584
		/**
1585
		 * Find apropirate column
1586
		 */
1587
		foreach ($sort as $key => $value) {
1588
			if (empty($this->columns[$key])) {
1589
				throw new DataGridException("Column <$key> not found");
1590
			}
1591
1592
			$column = $this->columns[$key];
1593
			$new_sort = [$column->getSortingColumn() => $value];
1594
1595
			/**
1596
			 * Pagination may be reseted after sorting
1597
			 */
1598
			if ($column->sortableResetPagination()) {
1599
				$this->page = 1;
1600
				$this->saveSessionData('_grid_page', 1);
1601
			}
1602
1603
			/**
1604
			 * Custom sorting callback may be applied
1605
			 */
1606
			if ($column->getSortableCallback()) {
1607
				$this->sort_callback = $column->getSortableCallback();
1608
			}
1609
		}
1610
1611
		/**
1612
		 * Session stuff
1613
		 */
1614
		$this->sort = $new_sort;
1615
		$this->saveSessionData('_grid_sort', $this->sort);
1616
1617
		$this->reload(['table']);
1618
	}
1619
1620
1621
	/**
1622
	 * handler for reseting the filter
1623
	 * @return void
1624
	 */
1625
	public function handleResetFilter()
1626
	{
1627
		/**
1628
		 * Session stuff
1629
		 */
1630
		$this->deleteSesssionData('_grid_page');
1631
1632
		foreach ($this->getSessionData() as $key => $value) {
1633
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1634
				$this->deleteSesssionData($key);
1635
			}
1636
		}
1637
1638
		$this->filter = [];
1639
1640
		$this->reload(['grid']);
1641
	}
1642
1643
1644
	/**
1645
	 * Handler for export
1646
	 * @param  int $id Key for particular export class in array $this->exports
1647
	 * @return void
1648
	 */
1649
	public function handleExport($id)
1650
	{
1651
		if (!isset($this->exports[$id])) {
1652
			throw new Nette\Application\ForbiddenRequestException;
1653
		}
1654
1655
		if (!empty($this->columns_export_order)) {
1656
			$this->setColumnsOrder($this->columns_export_order);
1657
		}
1658
1659
		$export = $this->exports[$id];
1660
1661
		if ($export->isFiltered()) {
1662
			$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...
1663
			$filter    = $this->assableFilters();
1664
		} else {
1665
			$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...
1666
			$filter    = [];
1667
		}
1668
1669
		if (NULL === $this->dataModel) {
1670
			throw new DataGridException('You have to set a data source first.');
1671
		}
1672
1673
		$rows = [];
1674
1675
		$items = Nette\Utils\Callback::invokeArgs(
1676
			[$this->dataModel, 'filterData'], [
1677
				NULL,
1678
				new Sorting($this->sort, $this->sort_callback),
1679
				$filter
1680
			]
1681
		);
1682
1683
		foreach ($items as $item) {
1684
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1685
		}
1686
1687
		if ($export instanceof Export\ExportCsv) {
1688
			$export->invoke($rows, $this);
1689
		} else {
1690
			$export->invoke($items, $this);
1691
		}
1692
1693
		if ($export->isAjax()) {
1694
			$this->reload();
1695
		}
1696
	}
1697
1698
1699
	/**
1700
	 * Handler for getting children of parent item (e.g. category)
1701
	 * @param  int $parent
1702
	 * @return void
1703
	 */
1704 View Code Duplication
	public function handleGetChildren($parent)
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...
1705
	{
1706
		$this->setDataSource(
1707
			call_user_func($this->tree_view_children_callback, $parent)
1708
		);
1709
1710
		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...
1711
			$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...
1712
			$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...
1713
1714
			$this->redrawControl('items');
1715
1716
			$this->onRedraw();
1717
		} else {
1718
			$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...
1719
		}
1720
	}
1721
1722
1723
	/**
1724
	 * Handler for getting item detail
1725
	 * @param  mixed $id
1726
	 * @return void
1727
	 */
1728 View Code Duplication
	public function handleGetItemDetail($id)
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...
1729
	{
1730
		$this->template->add('toggle_detail', $id);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1731
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1732
1733
		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...
1734
			$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...
1735
			$this->redrawControl('items');
1736
1737
			$this->onRedraw();
1738
		} else {
1739
			$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...
1740
		}
1741
	}
1742
1743
1744
	/**
1745
	 * Handler for inline editing
1746
	 * @param  mixed $id
1747
	 * @param  mixed $key
1748
	 * @return void
1749
	 */
1750
	public function handleEdit($id, $key)
1751
	{
1752
		$column = $this->getColumn($key);
1753
		$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...
1754
1755
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1756
	}
1757
1758
1759
	/**
1760
	 * Redraw $this
1761
	 * @return void
1762
	 */
1763
	public function reload($snippets = [])
1764
	{
1765
		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...
1766
			$this->redrawControl('tbody');
1767
			$this->redrawControl('pagination');
1768
1769
			/**
1770
			 * manualy reset exports links...
1771
			 */
1772
			$this->resetExportsLinks();
1773
			$this->redrawControl('exports');
1774
1775
			foreach ($snippets as $snippet) {
1776
				$this->redrawControl($snippet);
1777
			}
1778
1779
			$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...
1780
1781
			$this->onRedraw();
1782
		} else {
1783
			$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...
1784
		}
1785
	}
1786
1787
1788
	/**
1789
	 * Handler for column status
1790
	 * @param  string $id
1791
	 * @param  string $key
1792
	 * @param  string $value
1793
	 * @return void
1794
	 */
1795
	public function handleChangeStatus($id, $key, $value)
1796
	{
1797
		if (empty($this->columns[$key])) {
1798
			throw new DataGridException("ColumnStatus[$key] does not exist");
1799
		}
1800
1801
		$this->columns[$key]->onChange($id, $value);
1802
	}
1803
1804
1805
	/**
1806
	 * Redraw just one row via ajax
1807
	 * @param  int   $id
1808
	 * @param  mixed $primary_where_column
1809
	 * @return void
1810
	 */
1811
	public function redrawItem($id, $primary_where_column = NULL)
1812
	{
1813
		$this->snippets_set = TRUE;
1814
1815
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1816
1817
		$this->redrawControl('items');
1818
		$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...
1819
1820
		$this->onRedraw();
1821
	}
1822
1823
1824
	/**
1825
	 * Tell datagrid to display all columns
1826
	 * @return void
1827
	 */
1828
	public function handleShowAllColumns()
1829
	{
1830
		$this->deleteSesssionData('_grid_hidden_columns');
1831
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
1832
1833
		$this->redrawControl();
1834
1835
		$this->onRedraw();
1836
	}
1837
1838
1839
	/**
1840
	 * Tell datagrid to display default columns
1841
	 * @return void
1842
	 */
1843
	public function handleShowDefaultColumns()
1844
	{
1845
		$this->deleteSesssionData('_grid_hidden_columns');
1846
		$this->saveSessionData('_grid_hidden_columns_manipulated', FALSE);
1847
1848
		$this->redrawControl();
1849
1850
		$this->onRedraw();
1851
	}
1852
1853
1854
	/**
1855
	 * Reveal particular column
1856
	 * @param  string $column
1857
	 * @return void
1858
	 */
1859 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...
1860
	{
1861
		$columns = $this->getSessionData('_grid_hidden_columns');
1862
1863
		if (!empty($columns)) {
1864
			$pos = array_search($column, $columns);
1865
1866
			if ($pos !== FALSE) {
1867
				unset($columns[$pos]);
1868
			}
1869
		}
1870
1871
		$this->saveSessionData('_grid_hidden_columns', $columns);
1872
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
1873
1874
		$this->redrawControl();
1875
1876
		$this->onRedraw();
1877
	}
1878
1879
1880
	/**
1881
	 * Notice datagrid to not display particular columns
1882
	 * @param  string $column
1883
	 * @return void
1884
	 */
1885 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...
1886
	{
1887
		/**
1888
		 * Store info about hiding a column to session
1889
		 */
1890
		$columns = $this->getSessionData('_grid_hidden_columns');
1891
1892
		if (empty($columns)) {
1893
			$columns = [$column];
1894
		} else if (!in_array($column, $columns)) {
1895
			array_push($columns, $column);
1896
		}
1897
1898
		$this->saveSessionData('_grid_hidden_columns', $columns);
1899
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
1900
1901
		$this->redrawControl();
1902
1903
		$this->onRedraw();
1904
	}
1905
1906
1907
	public function handleActionCallback($__key, $__id)
1908
	{
1909
		$action = $this->getAction($__key);
1910
1911
		if (!($action instanceof Column\ActionCallback)) {
1912
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1913
		}
1914
1915
		$action->onClick($__id);
1916
	}
1917
1918
1919
	/********************************************************************************
1920
	 *                                  PAGINATION                                  *
1921
	 ********************************************************************************/
1922
1923
1924
	/**
1925
	 * Set options of select "items_per_page"
1926
	 * @param array $items_per_page_list
1927
	 * @return static
1928
	 */
1929
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1930
	{
1931
		$this->items_per_page_list = $items_per_page_list;
1932
1933
		if ($include_all) {
1934
			$this->items_per_page_list[] = 'all';
1935
		}
1936
1937
		return $this;
1938
	}
1939
1940
1941
	/**
1942
	 * Paginator factory
1943
	 * @return Components\DataGridPaginator\DataGridPaginator
1944
	 */
1945
	public function createComponentPaginator()
1946
	{
1947
		/**
1948
		 * Init paginator
1949
		 */
1950
		$component = new Components\DataGridPaginator\DataGridPaginator(
1951
			$this->getTranslator()
1952
		);
1953
		$paginator = $component->getPaginator();
1954
1955
		$paginator->setPage($this->page);
1956
		$paginator->setItemsPerPage($this->getPerPage());
1957
1958
		return $component;
1959
	}
1960
1961
1962
	/**
1963
	 * PerPage form factory
1964
	 * @return Form
1965
	 */
1966
	public function createComponentPerPage()
1967
	{
1968
		$form = new Form;
1969
1970
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1971
			->setValue($this->getPerPage());
1972
1973
		$form->addSubmit('submit', '');
1974
1975
		$saveSessionData = [$this, 'saveSessionData'];
1976
1977
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1978
			/**
1979
			 * Session stuff
1980
			 */
1981
			$saveSessionData('_grid_per_page', $values->per_page);
1982
1983
			/**
1984
			 * Other stuff
1985
			 */
1986
			$this->per_page = $values->per_page;
1987
			$this->reload();
1988
		};
1989
1990
		return $form;
1991
	}
1992
1993
1994
	/**
1995
	 * Get parameter per_page
1996
	 * @return int
1997
	 */
1998
	public function getPerPage()
1999
	{
2000
		$items_per_page_list = $this->getItemsPerPageList();
2001
2002
		$per_page = $this->per_page ?: reset($items_per_page_list);
2003
2004
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
2005
			$per_page = reset($items_per_page_list);
2006
		}
2007
2008
		return $per_page;
2009
	}
2010
2011
2012
	/**
2013
	 * Get associative array of items_per_page_list
2014
	 * @return array
2015
	 */
2016
	public function getItemsPerPageList()
2017
	{
2018
		if (empty($this->items_per_page_list)) {
2019
			$this->setItemsPerPageList([10, 20, 50], TRUE);
2020
		}
2021
2022
		$list = array_flip($this->items_per_page_list);
2023
2024
		foreach ($list as $key => $value) {
2025
			$list[$key] = $key;
2026
		}
2027
2028
		if (array_key_exists('all', $list)) {
2029
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
2030
		}
2031
2032
		return $list;
2033
	}
2034
2035
2036
	/**
2037
	 * Order Grid to "be paginated"
2038
	 * @param bool $do
2039
	 * @return static
2040
	 */
2041
	public function setPagination($do)
2042
	{
2043
		$this->do_paginate = (bool) $do;
2044
2045
		return $this;
2046
	}
2047
2048
2049
	/**
2050
	 * Tell whether Grid is paginated
2051
	 * @return bool
2052
	 */
2053
	public function isPaginated()
2054
	{
2055
		return $this->do_paginate;
2056
	}
2057
2058
2059
	/**
2060
	 * Return current paginator class
2061
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
2062
	 */
2063
	public function getPaginator()
2064
	{
2065
		if ($this->isPaginated() && $this->per_page !== 'all') {
2066
			return $this['paginator'];
2067
		}
2068
2069
		return NULL;
2070
	}
2071
2072
2073
	/********************************************************************************
2074
	 *                                     I18N                                     *
2075
	 ********************************************************************************/
2076
2077
2078
	/**
2079
	 * Set datagrid translator
2080
	 * @param Nette\Localization\ITranslator $translator
2081
	 * @return static
2082
	 */
2083
	public function setTranslator(Nette\Localization\ITranslator $translator)
2084
	{
2085
		$this->translator = $translator;
2086
2087
		return $this;
2088
	}
2089
2090
2091
	/**
2092
	 * Get translator for datagrid
2093
	 * @return Nette\Localization\ITranslator
2094
	 */
2095
	public function getTranslator()
2096
	{
2097
		if (!$this->translator) {
2098
			$this->translator = new Localization\SimpleTranslator;
2099
		}
2100
2101
		return $this->translator;
2102
	}
2103
2104
2105
	/********************************************************************************
2106
	 *                                 COLUMNS ORDER                                *
2107
	 ********************************************************************************/
2108
2109
2110
	/**
2111
	 * Set order of datagrid columns
2112
	 * @param array $order
2113
	 * @return static
2114
	 */
2115
	public function setColumnsOrder($order)
2116
	{
2117
		$new_order = [];
2118
2119
		foreach ($order as $key) {
2120
			if (isset($this->columns[$key])) {
2121
				$new_order[$key] = $this->columns[$key];
2122
			}
2123
		}
2124
2125
		if (sizeof($new_order) === sizeof($this->columns)) {
2126
			$this->columns = $new_order;
2127
		} else {
2128
			throw new DataGridException('When changing columns order, you have to specify all columns');
2129
		}
2130
2131
		return $this;
2132
	}
2133
2134
2135
	/**
2136
	 * Columns order may be different for export and normal grid
2137
	 * @param array $order
2138
	 */
2139
	public function setColumnsExportOrder($order)
2140
	{
2141
		$this->columns_export_order = (array) $order;
2142
	}
2143
2144
2145
	/********************************************************************************
2146
	 *                                SESSION & URL                                 *
2147
	 ********************************************************************************/
2148
2149
2150
	/**
2151
	 * Find some unique session key name
2152
	 * @return string
2153
	 */
2154
	public function getSessionSectionName()
2155
	{
2156
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
2157
	}
2158
2159
2160
	/**
2161
	 * Should datagrid remember its filters/pagination/etc using session?
2162
	 * @param bool $remember
2163
	 * @return static
2164
	 */
2165
	public function setRememberState($remember = TRUE)
2166
	{
2167
		$this->remember_state = (bool) $remember;
2168
2169
		return $this;
2170
	}
2171
2172
2173
	/**
2174
	 * Should datagrid refresh url using history API?
2175
	 * @param bool $refresh
2176
	 * @return static
2177
	 */
2178
	public function setRefreshUrl($refresh = TRUE)
2179
	{
2180
		$this->refresh_url = (bool) $refresh;
2181
2182
2183
		return $this;
2184
	}
2185
2186
2187
	/**
2188
	 * Get session data if functionality is enabled
2189
	 * @param  string $key
2190
	 * @return mixed
2191
	 */
2192
	public function getSessionData($key = NULL, $default_value = NULL)
2193
	{
2194
		if (!$this->remember_state) {
2195
			return $key ? $default_value : [];
2196
		}
2197
2198
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2199
	}
2200
2201
2202
	/**
2203
	 * Save session data - just if it is enabled
2204
	 * @param  string $key
2205
	 * @param  mixed  $value
2206
	 * @return void
2207
	 */
2208
	public function saveSessionData($key, $value)
2209
	{
2210
		if ($this->remember_state) {
2211
			$this->grid_session->{$key} = $value;
2212
		}
2213
	}
2214
2215
2216
	/**
2217
	 * Delete session data
2218
	 * @return void
2219
	 */
2220
	public function deleteSesssionData($key)
2221
	{
2222
		unset($this->grid_session->{$key});
2223
	}
2224
2225
2226
	/********************************************************************************
2227
	 *                                  ITEM DETAIL                                 *
2228
	 ********************************************************************************/
2229
2230
2231
	/**
2232
	 * Get items detail parameters
2233
	 * @return array
2234
	 */
2235
	public function getItemsDetail()
2236
	{
2237
		return $this->items_detail;
2238
	}
2239
2240
2241
	/**
2242
	 * Items can have thair detail - toggled
2243
	 * @param mixed $detail callable|string|bool
2244
	 * @param bool|NULL $primary_where_column
2245
	 * @return Column\ItemDetail
2246
	 */
2247
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2248
	{
2249
		if ($this->isSortable()) {
2250
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2251
		}
2252
2253
		$this->items_detail = new Column\ItemDetail(
2254
			$this,
2255
			$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...
2256
		);
2257
2258
		if (is_string($detail)) {
2259
			/**
2260
			 * Item detail will be in separate template
2261
			 */
2262
			$this->items_detail->setType('template');
2263
			$this->items_detail->setTemplate($detail);
2264
2265
		} else if (is_callable($detail)) {
2266
			/**
2267
			 * Item detail will be rendered via custom callback renderer
2268
			 */
2269
			$this->items_detail->setType('renderer');
2270
			$this->items_detail->setRenderer($detail);
2271
2272
		} else if (TRUE === $detail) {
2273
			/**
2274
			 * Item detail will be rendered probably via block #detail
2275
			 */
2276
			$this->items_detail->setType('block');
2277
2278
		} else {
2279
			throw new DataGridException(
2280
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2281
			);
2282
		}
2283
2284
		return $this->items_detail;
2285
	}
2286
2287
2288
	/**
2289
	 * @param callable $callable_set_container 
2290
	 * @return static
2291
	 */
2292
	public function setItemsDetailForm(callable $callable_set_container)
2293
	{
2294
		if ($this->items_detail instanceof Column\ItemDetail) {
2295
			$this->items_detail->setForm(
2296
				new Utils\ItemDetailForm($callable_set_container)
2297
			);
2298
2299
			return $this;
2300
		}
2301
2302
		throw new DataGridException('Please set the ItemDetail first.');
2303
	}
2304
2305
2306
	/**
2307
	 * @return Nette\Forms\Container|NULL
2308
	 */
2309
	public function getItemDetailForm()
2310
	{
2311
		if ($this->items_detail instanceof Column\ItemDetail) {
2312
			return $this->items_detail->getForm();
2313
		}
2314
2315
		return NULL;
2316
	}
2317
2318
2319
	/********************************************************************************
2320
	 *                                ROW PRIVILEGES                                *
2321
	 ********************************************************************************/
2322
2323
2324
	/**
2325
	 * @param  callable $condition
2326
	 * @return void
2327
	 */
2328
	public function allowRowsGroupAction(callable $condition)
2329
	{
2330
		$this->row_conditions['group_action'] = $condition;
2331
	}
2332
2333
2334
	/**
2335
	 * @param  string   $key
2336
	 * @param  callable $condition
2337
	 * @return void
2338
	 */
2339
	public function allowRowsAction($key, callable $condition)
2340
	{
2341
		$this->row_conditions['action'][$key] = $condition;
2342
	}
2343
2344
2345
	/**
2346
	 * @param  string      $name
2347
	 * @param  string|null $key
2348
	 * @return bool|callable
2349
	 */
2350
	public function getRowCondition($name, $key = NULL)
2351
	{
2352
		if (!isset($this->row_conditions[$name])) {
2353
			return FALSE;
2354
		}
2355
2356
		$condition = $this->row_conditions[$name];
2357
2358
		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...
2359
			return $condition;
2360
		}
2361
2362
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2363
	}
2364
2365
2366
	/********************************************************************************
2367
	 *                               COLUMN CALLBACK                                *
2368
	 ********************************************************************************/
2369
2370
2371
	/**
2372
	 * @param  string   $key
2373
	 * @param  callable $callback
2374
	 * @return void
2375
	 */
2376
	public function addColumnCallback($key, callable $callback)
2377
	{
2378
		$this->column_callbacks[$key] = $callback;
2379
	}
2380
2381
2382
	/**
2383
	 * @param  string $key
2384
	 * @return callable|null
2385
	 */
2386
	public function getColumnCallback($key)
2387
	{
2388
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
2389
	}
2390
2391
2392
	/********************************************************************************
2393
	 *                                 INLINE EDIT                                  *
2394
	 ********************************************************************************/
2395
2396
2397
	/**
2398
	 * @return InlineEdit
2399
	 */
2400
	public function addInlineEdit($primary_where_column = NULL)
2401
	{
2402
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2403
2404
		return $this->inlineEdit;
2405
	}
2406
2407
2408
	/**
2409
	 * @return InlineEdit|null
2410
	 */
2411
	public function getInlineEdit()
2412
	{
2413
		return $this->inlineEdit;
2414
	}
2415
2416
2417
	/**
2418
	 * @param  mixed $id
2419
	 * @return void
2420
	 */
2421
	public function handleInlineEdit($id)
2422
	{
2423
		if ($this->inlineEdit) {
2424
			$this->inlineEdit->setItemId($id);
2425
2426
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2427
2428
			$this['filter']['inline_edit']->addHidden('_id', $id);
2429
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2430
2431
			$this->redrawItem($id, $primary_where_column);
2432
		}
2433
	}
2434
2435
2436
	/********************************************************************************
2437
	 *                                  INLINE ADD                                  *
2438
	 ********************************************************************************/
2439
2440
2441
	/**
2442
	 * @return InlineEdit
2443
	 */
2444
	public function addInlineAdd()
2445
	{
2446
		$this->inlineAdd = new InlineEdit($this);
2447
2448
		$this->inlineAdd
2449
			->setIcon('plus')
2450
			->setClass('btn btn-xs btn-default');
2451
2452
		return $this->inlineAdd;
2453
	}
2454
2455
2456
	/**
2457
	 * @return InlineEdit|null
2458
	 */
2459
	public function getInlineAdd()
2460
	{
2461
		return $this->inlineAdd;
2462
	}
2463
2464
2465
	/********************************************************************************
2466
	 *                               HIDEABLE COLUMNS                               *
2467
	 ********************************************************************************/
2468
2469
2470
	/**
2471
	 * Can datagrid hide colums?
2472
	 * @return boolean
2473
	 */
2474
	public function canHideColumns()
2475
	{
2476
		return (bool) $this->can_hide_columns;
2477
	}
2478
2479
2480
	/**
2481
	 * Order Grid to set columns hideable.
2482
	 * @return static
2483
	 */
2484
	public function setColumnsHideable()
2485
	{
2486
		$this->can_hide_columns = TRUE;
2487
2488
		return $this;
2489
	}
2490
2491
2492
	/********************************************************************************
2493
	 *                                   INTERNAL                                   *
2494
	 ********************************************************************************/
2495
2496
2497
	/**
2498
	 * Get cont of columns
2499
	 * @return int
2500
	 */
2501
	public function getColumnsCount()
2502
	{
2503
		$count = sizeof($this->getColumns());
2504
2505
		if (!empty($this->actions)
2506
			|| $this->isSortable()
2507
			|| $this->getItemsDetail()
2508
			|| $this->getInlineEdit()
2509
			|| $this->getInlineAdd()) {
2510
			$count++;
2511
		}
2512
2513
		if ($this->hasGroupActions()) {
2514
			$count++;
2515
		}
2516
2517
		return $count;
2518
	}
2519
2520
2521
	/**
2522
	 * Get primary key of datagrid data source
2523
	 * @return string
2524
	 */
2525
	public function getPrimaryKey()
2526
	{
2527
		return $this->primary_key;
2528
	}
2529
2530
2531
	/**
2532
	 * Get set of set columns
2533
	 * @return Column\IColumn[]
2534
	 */
2535
	public function getColumns()
2536
	{
2537
		if (!$this->getSessionData('_grid_hidden_columns_manipulated', FALSE)) {
2538
			$columns_to_hide = [];
2539
2540
			foreach ($this->columns as $key => $column) {
2541
				if ($column->getDefaultHide()) {
2542
					$columns_to_hide[] = $key;
2543
				}
2544
			}
2545
2546
			if (!empty($columns_to_hide)) {
2547
				$this->saveSessionData('_grid_hidden_columns', $columns_to_hide);
2548
				$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2549
			}
2550
		}
2551
2552
		$hidden_columns = $this->getSessionData('_grid_hidden_columns', []);
2553
		
2554
		foreach ($hidden_columns as $column) {
2555
			if (!empty($this->columns[$column])) {
2556
				$this->columns_visibility[$column] = [
2557
					'visible' => FALSE,
2558
					'name' => $this->columns[$column]->getName()
2559
				];
2560
2561
				$this->removeColumn($column);
2562
			}
2563
		}
2564
2565
		return $this->columns;
2566
	}
2567
2568
2569
	/**
2570
	 * @return PresenterComponent
2571
	 */
2572
	public function getParent()
2573
	{
2574
		$parent = parent::getParent();
2575
2576
		if (!($parent instanceof PresenterComponent)) {
2577
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2578
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2579
			);
2580
		}
2581
2582
		return $parent;
2583
	}
2584
2585
}
2586