Completed
Push — master ( 83e9db...73ff60 )
by Pavel
02:56
created

DataGrid::hasSomeColumnDefaultHide()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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