Completed
Push — master ( 79e283...77c529 )
by Pavel
6s
created

DataGrid::hasTreeViewChildrenCallback()   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
18
class DataGrid extends Nette\Application\UI\Control
19
{
20
21
	/**
22
	 * @var callable[]
23
	 */
24
	public $onRedraw;
25
26
	/**
27
	 * @var string
28
	 */
29
	public static $icon_prefix = 'fa fa-';
30
31
	/**
32
	 * When set to TRUE, datagrid throws an exception
33
	 * 	when tring to get related entity within join and entity does not exist
34
	 * @var bool
35
	 */
36
	public $strict_entity_property = FALSE;
37
38
	/**
39
	 * @var int
40
	 * @persistent
41
	 */
42
	public $page = 1;
43
44
	/**
45
	 * @var int|string
46
	 * @persistent
47
	 */
48
	public $per_page;
49
50
	/**
51
	 * @var array
52
	 * @persistent
53
	 */
54
	public $sort = [];
55
56
	/**
57
	 * @var array
58
	 */
59
	public $default_sort = [];
60
61
	/**
62
	 * @var array
63
	 * @persistent
64
	 */
65
	public $filter = [];
66
67
	/**
68
	 * @var Callable[]
69
	 */
70
	public $onRender = [];
71
72
	/**
73
	 * @var bool
74
	 */
75
	protected $use_happy_components = TRUE;
76
77
	/**
78
	 * @var callable
79
	 */
80
	protected $rowCallback;
81
82
	/**
83
	 * @var array
84
	 */
85
	protected $items_per_page_list;
86
87
	/**
88
	 * @var string
89
	 */
90
	protected $template_file;
91
92
	/**
93
	 * @var Column\IColumn[]
94
	 */
95
	protected $columns = [];
96
97
	/**
98
	 * @var Column\Action[]
99
	 */
100
	protected $actions = [];
101
102
	/**
103
	 * @var GroupAction\GroupActionCollection
104
	 */
105
	protected $group_action_collection;
106
107
	/**
108
	 * @var Filter\Filter[]
109
	 */
110
	protected $filters = [];
111
112
	/**
113
	 * @var Export\Export[]
114
	 */
115
	protected $exports = [];
116
117
	/**
118
	 * @var DataModel
119
	 */
120
	protected $dataModel;
121
122
	/**
123
	 * @var DataFilter
124
	 */
125
	protected $dataFilter;
126
127
	/**
128
	 * @var string
129
	 */
130
	protected $primary_key = 'id';
131
132
	/**
133
	 * @var bool
134
	 */
135
	protected $do_paginate = TRUE;
136
137
	/**
138
	 * @var bool
139
	 */
140
	protected $csv_export = TRUE;
141
142
	/**
143
	 * @var bool
144
	 */
145
	protected $csv_export_filtered = TRUE;
146
147
	/**
148
	 * @var bool
149
	 */
150
	protected $sortable = FALSE;
151
152
	/**
153
	 * @var string
154
	 */
155
	protected $sortable_handler = 'sort!';
156
157
	/**
158
	 * @var string
159
	 */
160
	protected $original_template;
161
162
	/**
163
	 * @var array
164
	 */
165
	protected $redraw_item;
166
167
	/**
168
	 * @var mixed
169
	 */
170
	protected $translator;
171
172
	/**
173
	 * @var bool
174
	 */
175
	protected $force_filter_active;
176
177
	/**
178
	 * @var callable
179
	 */
180
	protected $tree_view_children_callback;
181
182
	/**
183
	 * @var callable
184
	 */
185
	protected $tree_view_has_children_callback;
186
187
	/**
188
	 * @var string
189
	 */
190
	protected $tree_view_has_children_column;
191
192
	/**
193
	 * @var bool
194
	 */
195
	protected $outer_filter_rendering = FALSE;
196
197
	/**
198
	 * @var array
199
	 */
200
	protected $columns_export_order = [];
201
202
	/**
203
	 * @var bool
204
	 */
205
	private $remember_state = TRUE;
206
207
	/**
208
	 * @var bool
209
	 */
210
	private $refresh_url = TRUE;
211
212
	/**
213
	 * @var Nette\Http\SessionSection
214
	 */
215
	private $grid_session;
216
217
	/**
218
	 * @var Column\ItemDetail
219
	 */
220
	private $items_detail;
221
222
	/**
223
	 * @var array
224
	 */
225
	private $row_conditions = [
226
		'group_action' => FALSE,
227
		'action' => []
228
	];
229
230
	/**
231
	 * @var bool
232
	 */
233
	protected $can_hide_columns = FALSE;
234
235
	/** @var callable[] */
236
	public $onColumnAdd;
237
238
239
	/**
240
	 * @param Nette\ComponentModel\IContainer|NULL $parent
241
	 * @param string                               $name
242
	 */
243
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
244
	{
245
		parent::__construct($parent, $name);
246
247
		$this->monitor('Nette\Application\UI\Presenter');
248
	}
249
250
251
	/**
252
	 * {inheritDoc}
253
	 * @return void
254
	 */
255
	public function attached($presenter)
256
	{
257
		parent::attached($presenter);
258
259
		if ($presenter instanceof Nette\Application\UI\Presenter) {
260
			/**
261
			 * Get session
262
			 */
263
			$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...
264
265
			/**
266
			 * Try to find previous filters/pagination/sort in session
267
			 */
268
			$this->findSessionFilters();
269
			$this->findDefaultSort();
270
		}
271
	}
272
273
274
	/********************************************************************************
275
	 *                                  RENDERING                                   *
276
	 ********************************************************************************/
277
278
279
	/**
280
	 * Render template
281
	 * @return void
282
	 */
283
	public function render()
284
	{
285
		/**
286
		 * Check whether datagrid has set some columns, initiated data source, etc
287
		 */
288
		if (!($this->dataModel instanceof DataModel)) {
289
			throw new DataGridException('You have to set a data source first.');
290
		}
291
292
		if (empty($this->columns)) {
293
			throw new DataGridException('You have to add at least one column.');
294
		}
295
296
		$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...
297
298
		/**
299
		 * Invoke some possible events
300
		 */
301
		$this->onRender($this);
0 ignored issues
show
Bug introduced by
The method onRender() does not exist on Ublaboo\DataGrid\DataGrid. Did you maybe mean render()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
302
303
		/**
304
		 * Prepare data for rendering (datagrid may render just one item)
305
		 */
306
		$rows = [];
307
308
		if (!empty($this->redraw_item)) {
309
			$items = $this->dataModel->filterRow($this->redraw_item);
310
		} else {
311
			$items = Nette\Utils\Callback::invokeArgs(
312
				[$this->dataModel, 'filterData'],
313
				[
314
					$this->getPaginator(),
315
					$this->sort,
316
					$this->assableFilters()
317
				]
318
			);
319
		}
320
321
		$callback = $this->rowCallback ?: NULL;
322
323
		foreach ($items as $item) {
324
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
325
326
			if ($callback) {
327
				$callback($item, $row->getControl());
328
			}
329
		}
330
331
		if ($this->isTreeView()) {
332
			$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...
333
		}
334
335
		$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...
336
337
		$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...
338
		$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...
339
		$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...
340
		$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...
341
342
		$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...
343
		$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...
344
		$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...
345
		$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...
346
347
		/**
348
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
349
		 */
350
		$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...
351
352
		/**
353
		 * Set template file and render it
354
		 */
355
		$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...
356
		$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...
357
	}
358
359
360
	/********************************************************************************
361
	 *                                 ROW CALLBACK                                 *
362
	 ********************************************************************************/
363
364
365
	/**
366
	 * Each row can be modified with user callback
367
	 * @param  callable  $callback
368
	 * @return static
369
	 */
370
	public function setRowCallback(callable $callback)
371
	{
372
		$this->rowCallback = $callback;
373
374
		return $this;
375
	}
376
377
378
	/********************************************************************************
379
	 *                                 DATA SOURCE                                  *
380
	 ********************************************************************************/
381
382
383
	/**
384
	 * By default ID, you can change that
385
	 * @param string $primary_key
386
	 * @return static
387
	 */
388
	public function setPrimaryKey($primary_key)
389
	{
390
		if ($this->dataModel instanceof DataModel) {
391
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
392
		}
393
394
		$this->primary_key = $primary_key;
395
396
		return $this;
397
	}
398
399
400
	/**
401
	 * Set Grid data source
402
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
403
	 * @return static
404
	 */
405
	public function setDataSource($source)
406
	{
407
		$this->dataModel = new DataModel($source, $this->primary_key);
408
409
		return $this;
410
	}
411
412
413
	/********************************************************************************
414
	 *                                  TEMPLATING                                  *
415
	 ********************************************************************************/
416
417
418
	/**
419
	 * Set custom template file to render
420
	 * @param string $template_file
421
	 * @return static
422
	 */
423
	public function setTemplateFile($template_file)
424
	{
425
		$this->template_file = $template_file;
426
427
		return $this;
428
	}
429
430
431
	/**
432
	 * Get DataGrid template file
433
	 * @return string
434
	 * @return static
435
	 */
436
	public function getTemplateFile()
437
	{
438
		return $this->template_file ?: $this->getOriginalTemplateFile();
439
	}
440
441
442
	/**
443
	 * Get DataGrid original template file
444
	 * @return string
445
	 */
446
	public function getOriginalTemplateFile()
447
	{
448
		return __DIR__.'/templates/datagrid.latte';
449
	}
450
451
452
	/**
453
	 * Tell datagrid wheteher to use or not happy components
454
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
455
	 * @return void|bool
456
	 */
457
	public function useHappyComponents($use = NULL)
458
	{
459
		if (NULL === $use) {
460
			return $this->use_happy_components;
461
		}
462
463
		$this->use_happy_components = (bool) $use;
464
	}
465
466
467
	/********************************************************************************
468
	 *                                   SORTING                                    *
469
	 ********************************************************************************/
470
471
472
	/**
473
	 * Set default sorting
474
	 * @param array $sort
475
	 * @return static
476
	 */
477
	public function setDefaultSort($sort)
478
	{
479
		if (is_string($sort)) {
480
			$sort = [$sort => 'ASC'];
481
		} else {
482
			$sort = (array) $sort;
483
		}
484
485
		$this->default_sort = $sort;
486
487
		return $this;
488
	}
489
490
491
	/**
492
	 * User may set default sorting, apply it
493
	 * @return void
494
	 */
495
	public function findDefaultSort()
496
	{
497
		if (!empty($this->sort)) {
498
			return;
499
		}
500
501
		if (!empty($this->default_sort)) {
502
			$this->sort = $this->default_sort;
503
		}
504
505
		$this->saveSessionData('_grid_sort', $this->sort);
506
	}
507
508
509
	/**
510
	 * Set grido to be sortable
511
	 * @param bool $sortable
512
	 * @return static
513
	 */
514
	public function setSortable($sortable = TRUE)
515
	{
516
		if ($this->getItemsDetail()) {
517
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
518
		}
519
520
		$this->sortable = (bool) $sortable;
521
522
		return $this;
523
	}
524
525
526
	/**
527
	 * Set sortable handle
528
	 * @param string $handler
529
	 * @return static
530
	 */
531
	public function setSortableHandler($handler = 'sort!')
532
	{
533
		$this->sortable_handler = (string) $handler;
534
535
		return $this;
536
	}
537
538
539
	/**
540
	 * Tell whether DataGrid is sortable
541
	 * @return bool
542
	 */
543
	public function isSortable()
544
	{
545
		return $this->sortable;
546
	}
547
548
	/**
549
	 * Return sortable handle name
550
	 * @return string
551
	 */
552
	public function getSortableHandler()
553
	{
554
		return $this->sortable_handler;
555
	}
556
557
558
	/********************************************************************************
559
	 *                                  TREE VIEW                                   *
560
	 ********************************************************************************/
561
562
563
	/**
564
	 * Is tree view set?
565
	 * @return boolean
566
	 */
567
	public function isTreeView()
568
	{
569
		return (bool) $this->tree_view_children_callback;
570
	}
571
572
573
	/**
574
	 * Setting tree view
575
	 * @param callable $get_children_callback
576
	 * @param string|callable $tree_view_has_children_column
577
	 * @return static
578
	 */
579
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
580
	{
581
		if (!is_callable($get_children_callback)) {
582
			throw new DataGridException(
583
				'Parameters to method DataGrid::setTreeView must be of type callable'
584
			);
585
		}
586
587
		if (is_callable($tree_view_has_children_column)) {
588
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
589
			$tree_view_has_children_column = null;
590
		}
591
592
		$this->tree_view_children_callback = $get_children_callback;
593
		$this->tree_view_has_children_column = $tree_view_has_children_column;
594
595
		/**
596
		 * TUrn off pagination
597
		 */
598
		$this->setPagination(FALSE);
599
600
		/**
601
		 * Set tree view template file
602
		 */
603
		if (!$this->template_file) {
604
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
605
		}
606
607
		return $this;
608
	}
609
610
	/**
611
	 * Is tree view children callback set?
612
	 * @return boolean
613
	 */
614
	public function hasTreeViewChildrenCallback()
615
	{
616
		return is_callable($this->tree_view_has_children_callback);
617
	}
618
619
	/**
620
	 * @param $primary_key
621
	 * @return boolean
622
	 */
623
	public function treeViewChildrenCallback($primary_key)
624
	{
625
		return call_user_func($this->tree_view_has_children_callback, $primary_key);
626
	}
627
628
629
	/********************************************************************************
630
	 *                                    COLUMNS                                   *
631
	 ********************************************************************************/
632
633
634
	/**
635
	 * Add text column with no other formating
636
	 * @param  string      $key
637
	 * @param  string      $name
638
	 * @param  string|null $column
639
	 * @return Column\ColumnText
640
	 */
641
	public function addColumnText($key, $name, $column = NULL)
642
	{
643
		$this->addColumnCheck($key);
644
		$column = $column ?: $key;
645
646
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
647
	}
648
649
650
	/**
651
	 * Add column with link
652
	 * @param  string      $key
653
	 * @param  string      $name
654
	 * @param  string|null $column
655
	 * @return Column\ColumnLink
656
	 */
657
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
658
	{
659
		$this->addColumnCheck($key);
660
		$column = $column ?: $key;
661
		$href = $href ?: $key;
662
663
		if (NULL === $params) {
664
			$params = [$this->primary_key];
665
		}
666
667
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
668
	}
669
670
671
	/**
672
	 * Add column with possible number formating
673
	 * @param  string      $key
674
	 * @param  string      $name
675
	 * @param  string|null $column
676
	 * @return Column\ColumnNumber
677
	 */
678
	public function addColumnNumber($key, $name, $column = NULL)
679
	{
680
		$this->addColumnCheck($key);
681
		$column = $column ?: $key;
682
683
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
684
	}
685
686
687
	/**
688
	 * Add column with date formating
689
	 * @param  string      $key
690
	 * @param  string      $name
691
	 * @param  string|null $column
692
	 * @return Column\ColumnDateTime
693
	 */
694
	public function addColumnDateTime($key, $name, $column = NULL)
695
	{
696
		$this->addColumnCheck($key);
697
		$column = $column ?: $key;
698
699
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
700
	}
701
702
703
	/**
704
	 * Add column status
705
	 * @param  string      $key
706
	 * @param  string      $name
707
	 * @param  string|null $column
708
	 * @return Column\ColumnStatus
709
	 */
710
	public function addColumnStatus($key, $name, $column = NULL)
711
	{
712
		$this->addColumnCheck($key);
713
		$column = $column ?: $key;
714
715
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
716
	}
717
718
719
	/**
720
	 * @param string $key
721
	 * @param Column\Column $column
722
	 * @return Column\Column
723
	 */
724
	protected function addColumn($key, Column\Column $column)
725
	{
726
		$this->onColumnAdd($key, $column);
0 ignored issues
show
Documentation Bug introduced by
The method onColumnAdd does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
727
728
		return $this->columns[$key] = $column;
729
	}
730
731
732
	/**
733
	 * Return existing column
734
	 * @param  string $key
735
	 * @return Column\Column
736
	 * @throws DataGridException
737
	 */
738
	public function getColumn($key)
739
	{
740
		if (!isset($this->columns[$key])) {
741
			throw new DataGridException("There is no column at key [$key] defined.");
742
		}
743
744
		return $this->columns[$key];
745
	}
746
747
748
	/**
749
	 * Remove column
750
	 * @param string $key
751
	 * @return void
752
	 */
753
	public function removeColumn($key)
754
	{
755
		unset($this->columns[$key]);
756
	}
757
758
759
	/**
760
	 * Check whether given key already exists in $this->columns
761
	 * @param  string $key
762
	 * @throws DataGridException
763
	 */
764
	protected function addColumnCheck($key)
765
	{
766
		if (isset($this->columns[$key])) {
767
			throw new DataGridException("There is already column at key [$key] defined.");
768
		}
769
	}
770
771
772
	/********************************************************************************
773
	 *                                    ACTIONS                                   *
774
	 ********************************************************************************/
775
776
777
	/**
778
	 * Create action
779
	 * @param string     $key
780
	 * @param string     $name
781
	 * @param string     $href
782
	 * @param array|null $params
783
	 * @return Column\Action
784
	 */
785
	public function addAction($key, $name, $href = NULL, array $params = NULL)
786
	{
787
		$this->addActionCheck($key);
788
		$href = $href ?: $key;
789
790
		if (NULL === $params) {
791
			$params = [$this->primary_key];
792
		}
793
794
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
795
	}
796
797
798
	/**
799
	 * Create action callback
800
	 * @param string     $key
801
	 * @param string     $name
802
	 * @return Column\Action
803
	 */
804
	public function addActionCallback($key, $name, $callback = NULL)
805
	{
806
		$this->addActionCheck($key);
807
		$params = ['__id' => $this->primary_key];
808
809
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
810
811
		if ($callback) {
812
			if (!is_callable($callback)) {
813
				throw new DataGridException('ActionCallback callback has to be callable.');
814
			}
815
816
			$action->onClick[] = $callback;
817
		}
818
819
		return $action;
820
	}
821
822
823
	/**
824
	 * Get existing action
825
	 * @param  string       $key
826
	 * @return Column\Action
827
	 * @throws DataGridException
828
	 */
829
	public function getAction($key)
830
	{
831
		if (!isset($this->actions[$key])) {
832
			throw new DataGridException("There is no action at key [$key] defined.");
833
		}
834
835
		return $this->actions[$key];
836
	}
837
838
839
	/**
840
	 * Remove action
841
	 * @param string $key
842
	 * @return void
843
	 */
844
	public function removeAction($key)
845
	{
846
		unset($this->actions[$key]);
847
	}
848
849
850
	/**
851
	 * Check whether given key already exists in $this->filters
852
	 * @param  string $key
853
	 * @throws DataGridException
854
	 */
855
	protected function addActionCheck($key)
856
	{
857
		if (isset($this->actions[$key])) {
858
			throw new DataGridException("There is already action at key [$key] defined.");
859
		}
860
	}
861
862
863
	/********************************************************************************
864
	 *                                    FILTERS                                   *
865
	 ********************************************************************************/
866
867
868
	/**
869
	 * Add filter fot text search
870
	 * @param string       $key
871
	 * @param string       $name
872
	 * @param array|string $columns
873
	 * @return Filter\FilterText
874
	 * @throws DataGridException
875
	 */
876
	public function addFilterText($key, $name, $columns = NULL)
877
	{
878
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
879
880
		if (!is_array($columns)) {
881
			throw new DataGridException("Filter Text can except only array or string.");
882
		}
883
884
		$this->addFilterCheck($key);
885
886
		return $this->filters[$key] = new Filter\FilterText($key, $name, $columns);
887
	}
888
889
890
	/**
891
	 * Add select box filter
892
	 * @param string $key
893
	 * @param string $name
894
	 * @param array  $options
895
	 * @param string $column
896
	 * @return Filter\FilterSelect
897
	 * @throws DataGridException
898
	 */
899 View Code Duplication
	public function addFilterSelect($key, $name, $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...
900
	{
901
		$column = $column ?: $key;
902
903
		if (!is_string($column)) {
904
			throw new DataGridException("Filter Select can only filter through one column.");
905
		}
906
907
		$this->addFilterCheck($key);
908
909
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
910
	}
911
912
913
	/**
914
	 * Add datepicker filter
915
	 * @param string $key
916
	 * @param string $name
917
	 * @param string $column
918
	 * @return Filter\FilterDate
919
	 * @throws DataGridException
920
	 */
921
	public function addFilterDate($key, $name, $column = NULL)
922
	{
923
		$column = $column ?: $key;
924
925
		if (!is_string($column)) {
926
			throw new DataGridException("FilterDate can only filter through one column.");
927
		}
928
929
		$this->addFilterCheck($key);
930
931
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
932
	}
933
934
935
	/**
936
	 * Add range filter (from - to)
937
	 * @param string $key
938
	 * @param string $name
939
	 * @param string $column
940
	 * @return Filter\FilterRange
941
	 * @throws DataGridException
942
	 */
943 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...
944
	{
945
		$column = $column ?: $key;
946
947
		if (!is_string($column)) {
948
			throw new DataGridException("FilterRange can only filter through one column.");
949
		}
950
951
		$this->addFilterCheck($key);
952
953
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
954
	}
955
956
957
	/**
958
	 * Add datepicker filter (from - to)
959
	 * @param string $key
960
	 * @param string $name
961
	 * @param string $column
962
	 * @return Filter\FilterDateRange
963
	 * @throws DataGridException
964
	 */
965 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...
966
	{
967
		$column = $column ?: $key;
968
969
		if (!is_string($column)) {
970
			throw new DataGridException("FilterDateRange can only filter through one column.");
971
		}
972
973
		$this->addFilterCheck($key);
974
975
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
976
	}
977
978
979
	/**
980
	 * Check whether given key already exists in $this->filters
981
	 * @param  string $key
982
	 * @throws DataGridException
983
	 */
984
	protected function addFilterCheck($key)
985
	{
986
		if (isset($this->filters[$key])) {
987
			throw new DataGridException("There is already action at key [$key] defined.");
988
		}
989
	}
990
991
992
	/**
993
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
994
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
995
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
996
	 */
997
	public function assableFilters()
998
	{
999
		foreach ($this->filter as $key => $value) {
1000
			if (!isset($this->filters[$key])) {
1001
				$this->deleteSesssionData($key);
1002
1003
				continue;
1004
			}
1005
1006
			if (is_array($value) || $value instanceof \Traversable) {
1007
				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...
1008
					$this->filters[$key]->setValue($value);
1009
				}
1010
			} else {
1011
				if ($value !== '' && $value !== NULL) {
1012
					$this->filters[$key]->setValue($value);
1013
				}
1014
			}
1015
		}
1016
1017
		foreach ($this->columns as $column) {
1018
			if (isset($this->sort[$column->getSortingColumn()])) {
1019
				$column->setSort($this->sort);
1020
			}
1021
		}
1022
1023
		return $this->filters;
1024
	}
1025
1026
1027
	/**
1028
	 * Remove filter
1029
	 * @param string $key
1030
	 * @return void
1031
	 */
1032
	public function removeFilter($key)
1033
	{
1034
		unset($this->filters[$key]);
1035
	}
1036
1037
1038
	/********************************************************************************
1039
	 *                                  FILTERING                                   *
1040
	 ********************************************************************************/
1041
1042
1043
	/**
1044
	 * Is filter active?
1045
	 * @return boolean
1046
	 */
1047
	public function isFilterActive()
1048
	{
1049
		$is_filter = ArraysHelper::testTruthy($this->filter);
1050
1051
		return ($is_filter) || $this->force_filter_active;
1052
	}
1053
1054
1055
	/**
1056
	 * Tell that filter is active from whatever reasons
1057
	 * return static
1058
	 */
1059
	public function setFilterActive()
1060
	{
1061
		$this->force_filter_active = TRUE;
1062
1063
		return $this;
1064
	}
1065
1066
1067
	/**
1068
	 * If we want to sent some initial filter
1069
	 * @param array $filter
1070
	 * @return static
1071
	 */
1072
	public function setFilter(array $filter)
1073
	{
1074
		$this->filter = $filter;
1075
1076
		return $this;
1077
	}
1078
1079
1080
	/**
1081
	 * FilterAndGroupAction form factory
1082
	 * @return Form
1083
	 */
1084
	public function createComponentFilter()
1085
	{
1086
		$form = new Form($this, 'filter');
1087
1088
		$form->setTranslator($this->getTranslator());
1089
1090
		$form->setMethod('get');
1091
1092
		/**
1093
		 * Filter part
1094
		 */
1095
		$filter_container = $form->addContainer('filter');
1096
1097
		foreach ($this->filters as $filter) {
1098
			$filter->addToFormContainer($filter_container);
1099
		}
1100
1101
		/**
1102
		 * Group action part
1103
		 */
1104
		$group_action_container = $form->addContainer('group_action');
1105
1106
		if ($this->hasGroupActions()) {
1107
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1108
		}
1109
1110
		$form->setDefaults(['filter' => $this->filter]);
1111
1112
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1113
1114
		return $form;
1115
	}
1116
1117
1118
	/**
1119
	 * Set $this->filter values after filter form submitted
1120
	 * @param  Form $form
1121
	 * @return void
1122
	 */
1123
	public function filterSucceeded(Form $form)
1124
	{
1125
		$values = $form->getValues();
1126
1127
		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...
1128
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1129
				return;
1130
			}
1131
		}
1132
1133
		$values = $values['filter'];
1134
1135
		foreach ($values as $key => $value) {
1136
			/**
1137
			 * Session stuff
1138
			 */
1139
			$this->saveSessionData($key, $value);
1140
1141
			/**
1142
			 * Other stuff
1143
			 */
1144
			$this->filter[$key] = $value;
1145
		}
1146
1147
		$this->reload();
1148
	}
1149
1150
1151
	/**
1152
	 * Should be datagrid filters rendered separately?
1153
	 * @param boolean $out
1154
	 * @return static
1155
	 */
1156
	public function setOuterFilterRendering($out = TRUE)
1157
	{
1158
		$this->outer_filter_rendering = (bool) $out;
1159
1160
		return $this;
1161
	}
1162
1163
1164
	/**
1165
	 * Are datagrid filters rendered separately?
1166
	 * @return boolean
1167
	 */
1168
	public function hasOuterFilterRendering()
1169
	{
1170
		return $this->outer_filter_rendering;
1171
	}
1172
1173
1174
	/**
1175
	 * Try to restore session stuff
1176
	 * @return void
1177
	 */
1178
	public function findSessionFilters()
1179
	{
1180
		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...
1181
			return;
1182
		}
1183
1184
		if (!$this->remember_state) {
1185
			return;
1186
		}
1187
1188
		if ($page = $this->getSessionData('_grid_page')) {
1189
			$this->page = $page;
1190
		}
1191
1192
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1193
			$this->per_page = $per_page;
1194
		}
1195
1196
		if ($sort = $this->getSessionData('_grid_sort')) {
1197
			$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...
1198
		}
1199
1200
		foreach ($this->getSessionData() as $key => $value) {
1201
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1202
				$this->filter[$key] = $value;
1203
			}
1204
		}
1205
	}
1206
1207
1208
	/********************************************************************************
1209
	 *                                    EXPORTS                                   *
1210
	 ********************************************************************************/
1211
1212
1213
	/**
1214
	 * Add export of type callback
1215
	 * @param string $text
1216
	 * @param callable $callback
1217
	 * @param boolean $filtered
1218
	 * @return Export\Export
1219
	 */
1220
	public function addExportCallback($text, $callback, $filtered = FALSE)
1221
	{
1222
		if (!is_callable($callback)) {
1223
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1224
		}
1225
1226
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1227
	}
1228
1229
1230
	/**
1231
	 * Add already implemented csv export
1232
	 * @param string $text
1233
	 * @param string $csv_file_name
1234
	 * @return Export\Export
1235
	 */
1236
	public function addExportCsv($text, $csv_file_name)
1237
	{
1238
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1239
	}
1240
1241
1242
	/**
1243
	 * Add already implemented csv export, but for filtered data
1244
	 * @param string $text
1245
	 * @param string $csv_file_name
1246
	 * @return Export\Export
1247
	 */
1248
	public function addExportCsvFiltered($text, $csv_file_name)
1249
	{
1250
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1251
	}
1252
1253
1254
	/**
1255
	 * Add export to array
1256
	 * @param Export\Export $export
1257
	 * @return Export\Export
1258
	 */
1259
	protected function addToExports(Export\Export $export)
1260
	{
1261
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1262
1263
		$export->setLink($this->link('export!', ['id' => $id]));
1264
1265
		return $this->exports[$id] = $export;
1266
	}
1267
1268
1269
	public function resetExportsLinks()
1270
	{
1271
		foreach ($this->exports as $id => $export) {
1272
			$export->setLink($this->link('export!', ['id' => $id]));
1273
		}
1274
	}
1275
1276
1277
	/********************************************************************************
1278
	 *                                 GROUP ACTIONS                                *
1279
	 ********************************************************************************/
1280
1281
1282
	/**
1283
	 * Add group actino
1284
	 * @param string $title
1285
	 * @param array  $options
1286
	 * @return GroupAction\GroupAction
1287
	 */
1288
	public function addGroupAction($title, $options = [])
1289
	{
1290
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1291
	}
1292
1293
1294
	/**
1295
	 * Get collection of all group actions
1296
	 * @return GroupAction\GroupActionCollection
1297
	 */
1298
	public function getGroupActionCollection()
1299
	{
1300
		if (!$this->group_action_collection) {
1301
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1302
		}
1303
1304
		return $this->group_action_collection;
1305
	}
1306
1307
1308
	/**
1309
	 * Has datagrid some group actions?
1310
	 * @return boolean
1311
	 */
1312
	public function hasGroupActions()
1313
	{
1314
		return (bool) $this->group_action_collection;
1315
	}
1316
1317
1318
	/********************************************************************************
1319
	 *                                   HANDLERS                                   *
1320
	 ********************************************************************************/
1321
1322
1323
	/**
1324
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1325
	 * @param  int  $page
1326
	 * @return void
1327
	 */
1328
	public function handlePage($page)
1329
	{
1330
		/**
1331
		 * Session stuff
1332
		 */
1333
		$this->page = $page;
1334
		$this->saveSessionData('_grid_page', $page);
1335
1336
		$this->reload(['table']);
1337
	}
1338
1339
1340
	/**
1341
	 * Handler for sorting
1342
	 * @param array $sort
1343
	 * @return void
1344
	 */
1345
	public function handleSort(array $sort)
1346
	{
1347
		$new_sort = [];
1348
1349
		/**
1350
		 * Find apropirate column
1351
		 */
1352
		foreach ($sort as $key => $value) {
1353
			if (empty($this->columns[$key])) {
1354
				throw new DataGridException("Column <$key> not found");
1355
			}
1356
1357
			$column = $this->columns[$key];
1358
			$new_sort = [$column->getSortingColumn() => $value];
1359
		}
1360
1361
		/**
1362
		 * Session stuff
1363
		 */
1364
		$this->sort = $new_sort;
1365
		$this->saveSessionData('_grid_sort', $this->sort);
1366
1367
		$this->reload(['table']);
1368
	}
1369
1370
1371
	/**
1372
	 * handler for reseting the filter
1373
	 * @return void
1374
	 */
1375
	public function handleResetFilter()
1376
	{
1377
		/**
1378
		 * Session stuff
1379
		 */
1380
		$this->deleteSesssionData('_grid_page');
1381
1382
		foreach ($this->getSessionData() as $key => $value) {
1383
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1384
				$this->deleteSesssionData($key);
1385
			}
1386
		}
1387
1388
		$this->filter = [];
1389
1390
		$this->reload(['grid']);
1391
	}
1392
1393
1394
	/**
1395
	 * Handler for export
1396
	 * @param  int $id Key for particular export class in array $this->exports
1397
	 * @return void
1398
	 */
1399
	public function handleExport($id)
1400
	{
1401
		if (!isset($this->exports[$id])) {
1402
			throw new Nette\Application\ForbiddenRequestException;
1403
		}
1404
1405
		if (!empty($this->columns_export_order)) {
1406
			$this->setColumnsOrder($this->columns_export_order);
1407
		}
1408
1409
		$export = $this->exports[$id];
1410
1411
		if ($export->isFiltered()) {
1412
			$sort      = $this->sort;
1413
			$filter    = $this->assableFilters();
1414
		} else {
1415
			$sort      = [$this->primary_key => 'ASC'];
1416
			$filter    = [];
1417
		}
1418
1419
		if (NULL === $this->dataModel) {
1420
			throw new DataGridException('You have to set a data source first.');
1421
		}
1422
1423
		$rows = [];
1424
1425
		$items = Nette\Utils\Callback::invokeArgs(
1426
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1427
		);
1428
1429
		foreach ($items as $item) {
1430
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1431
		}
1432
1433
		if ($export instanceof Export\ExportCsv) {
1434
			$export->invoke($rows, $this);
1435
		} else {
1436
			$export->invoke($items, $this);
1437
		}
1438
1439
		if ($export->isAjax()) {
1440
			$this->reload();
1441
		}
1442
	}
1443
1444
1445
	/**
1446
	 * Handler for getting children of parent item (e.g. category)
1447
	 * @param  int $parent
1448
	 * @return void
1449
	 */
1450
	public function handleGetChildren($parent)
1451
	{
1452
		$this->setDataSource(
1453
			call_user_func($this->tree_view_children_callback, $parent)
1454
		);
1455
1456
		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...
1457
			$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...
1458
			$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...
1459
1460
			$this->redrawControl('items');
1461
1462
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1463
		} else {
1464
			$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...
1465
		}
1466
	}
1467
1468
1469
	/**
1470
	 * Handler for getting item detail
1471
	 * @param  mixed $id
1472
	 * @return void
1473
	 */
1474
	public function handleGetItemDetail($id)
1475
	{
1476
		$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...
1477
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1478
1479
		if ($this->getPresenter()->isAjax()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method isAjax() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1480
			$this->getPresenter()->payload->_datagrid_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...
1481
			$this->redrawControl('items');
1482
1483
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1484
		} else {
1485
			$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...
1486
		}
1487
	}
1488
1489
1490
	/**
1491
	 * Handler for inline editing
1492
	 * @param  mixed $id
1493
	 * @param  mixed $key
1494
	 * @return void
1495
	 */
1496
	public function handleEdit($id, $key)
1497
	{
1498
		$column = $this->getColumn($key);
1499
		$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...
1500
1501
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1502
	}
1503
1504
1505
	/**
1506
	 * Redraw $this
1507
	 * @return void
1508
	 */
1509
	public function reload($snippets = [])
1510
	{
1511
		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...
1512
			$this->redrawControl('tbody');
1513
			$this->redrawControl('pagination');
1514
1515
			/**
1516
			 * manualy reset exports links...
1517
			 */
1518
			$this->resetExportsLinks();
1519
			$this->redrawControl('exports');
1520
1521
			foreach ($snippets as $snippet) {
1522
				$this->redrawControl($snippet);
1523
			}
1524
1525
			$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...
1526
1527
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1528
		} else {
1529
			$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...
1530
		}
1531
	}
1532
1533
1534
	/**
1535
	 * Handler for column status
1536
	 * @param  string $id
1537
	 * @param  string $key
1538
	 * @param  string $value
1539
	 * @return void
1540
	 */
1541
	public function handleChangeStatus($id, $key, $value)
1542
	{
1543
		if (empty($this->columns[$key])) {
1544
			throw new DataGridException("ColumnStatus[$key] does not exist");
1545
		}
1546
1547
		$this->columns[$key]->onChange($id, $value);
1548
	}
1549
1550
1551
	/**
1552
	 * Redraw just one row via ajax
1553
	 * @param  int   $id
1554
	 * @param  mixed $primary_where_column
1555
	 * @return void
1556
	 */
1557
	public function redrawItem($id, $primary_where_column = NULL)
1558
	{
1559
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1560
1561
		$this->redrawControl('items');
1562
		$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...
1563
1564
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1565
	}
1566
1567
1568
	/**
1569
	 * Tell datagrid to display all columns
1570
	 * @return void
1571
	 */
1572
	public function handleShowAllColumns()
1573
	{
1574
		$this->deleteSesssionData('_grid_hidden_columns');
1575
1576
		$this->redrawControl();
1577
1578
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1579
	}
1580
1581
1582
	/**
1583
	 * Notice datagrid to not display particular columns
1584
	 * @param  string $column
1585
	 * @return void
1586
	 */
1587
	public function handleHideColumn($column)
1588
	{
1589
		/**
1590
		 * Store info about hiding a column to session
1591
		 */
1592
		$columns = $this->getSessionData('_grid_hidden_columns');
1593
1594
		if (empty($columns)) {
1595
			$columns = [$column];
1596
		} else if (!in_array($column, $columns)) {
1597
			array_push($columns, $column);
1598
		}
1599
1600
		$this->saveSessionData('_grid_hidden_columns', $columns);
1601
1602
		$this->redrawControl();
1603
1604
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1605
	}
1606
1607
1608
	public function handleActionCallback($__key, $__id)
1609
	{
1610
		$action = $this->getAction($__key);
1611
1612
		if (!($action instanceof Column\ActionCallback)) {
1613
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1614
		}
1615
1616
		$action->onClick($__id);
1617
	}
1618
1619
1620
	/********************************************************************************
1621
	 *                                  PAGINATION                                  *
1622
	 ********************************************************************************/
1623
1624
1625
	/**
1626
	 * Set options of select "items_per_page"
1627
	 * @param array $items_per_page_list
1628
	 * @return static
1629
	 */
1630
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1631
	{
1632
		$this->items_per_page_list = $items_per_page_list;
1633
1634
		if ($include_all) {
1635
			$this->items_per_page_list[] = 'all';
1636
		}
1637
1638
		return $this;
1639
	}
1640
1641
1642
	/**
1643
	 * Paginator factory
1644
	 * @return Components\DataGridPaginator\DataGridPaginator
1645
	 */
1646
	public function createComponentPaginator()
1647
	{
1648
		/**
1649
		 * Init paginator
1650
		 */
1651
		$component = new Components\DataGridPaginator\DataGridPaginator(
1652
			$this->getTranslator()
1653
		);
1654
		$paginator = $component->getPaginator();
1655
1656
		$paginator->setPage($this->page);
1657
		$paginator->setItemsPerPage($this->getPerPage());
1658
1659
		return $component;
1660
	}
1661
1662
1663
	/**
1664
	 * PerPage form factory
1665
	 * @return Form
1666
	 */
1667
	public function createComponentPerPage()
1668
	{
1669
		$form = new Form;
1670
1671
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1672
			->setValue($this->getPerPage());
1673
1674
		$form->addSubmit('submit', '');
1675
1676
		$saveSessionData = [$this, 'saveSessionData'];
1677
1678
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1679
			/**
1680
			 * Session stuff
1681
			 */
1682
			$saveSessionData('_grid_per_page', $values->per_page);
1683
1684
			/**
1685
			 * Other stuff
1686
			 */
1687
			$this->per_page = $values->per_page;
1688
			$this->reload();
1689
		};
1690
1691
		return $form;
1692
	}
1693
1694
1695
	/**
1696
	 * Get parameter per_page
1697
	 * @return int
1698
	 */
1699
	public function getPerPage()
1700
	{
1701
		$items_per_page_list = $this->getItemsPerPageList();
1702
1703
		$per_page = $this->per_page ?: reset($items_per_page_list);
1704
1705
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
1706
			$per_page = reset($items_per_page_list);
1707
		}
1708
1709
		return $per_page;
1710
	}
1711
1712
1713
	/**
1714
	 * Get associative array of items_per_page_list
1715
	 * @return array
1716
	 */
1717
	public function getItemsPerPageList()
1718
	{
1719
		if (empty($this->items_per_page_list)) {
1720
			$this->setItemsPerPageList([10, 20, 50], TRUE);
1721
		}
1722
1723
		$list = array_flip($this->items_per_page_list);
1724
1725
		foreach ($list as $key => $value) {
1726
			$list[$key] = $key;
1727
		}
1728
1729
		if (array_key_exists('all', $list)) {
1730
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
1731
		}
1732
1733
		return $list;
1734
	}
1735
1736
1737
	/**
1738
	 * Order Grid to "be paginated"
1739
	 * @param bool $do
1740
	 * @return static
1741
	 */
1742
	public function setPagination($do)
1743
	{
1744
		$this->do_paginate = (bool) $do;
1745
1746
		return $this;
1747
	}
1748
1749
1750
	/**
1751
	 * Tell whether Grid is paginated
1752
	 * @return bool
1753
	 */
1754
	public function isPaginated()
1755
	{
1756
		return $this->do_paginate;
1757
	}
1758
1759
1760
	/**
1761
	 * Return current paginator class
1762
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1763
	 */
1764
	public function getPaginator()
1765
	{
1766
		if ($this->isPaginated() && $this->per_page !== 'all') {
1767
			return $this['paginator'];
1768
		}
1769
1770
		return NULL;
1771
	}
1772
1773
1774
	/********************************************************************************
1775
	 *                                     I18N                                     *
1776
	 ********************************************************************************/
1777
1778
1779
	/**
1780
	 * Set datagrid translator
1781
	 * @param Nette\Localization\ITranslator $translator
1782
	 * @return static
1783
	 */
1784
	public function setTranslator(Nette\Localization\ITranslator $translator)
1785
	{
1786
		$this->translator = $translator;
1787
1788
		return $this;
1789
	}
1790
1791
1792
	/**
1793
	 * Get translator for datagrid
1794
	 * @return Nette\Localization\ITranslator
1795
	 */
1796
	public function getTranslator()
1797
	{
1798
		if (!$this->translator) {
1799
			$this->translator = new Localization\SimpleTranslator;
1800
		}
1801
1802
		return $this->translator;
1803
	}
1804
1805
1806
	/********************************************************************************
1807
	 *                                 COLUMNS ORDER                                *
1808
	 ********************************************************************************/
1809
1810
1811
	/**
1812
	 * Set order of datagrid columns
1813
	 * @param array $order
1814
	 * @return static
1815
	 */
1816
	public function setColumnsOrder($order)
1817
	{
1818
		$new_order = [];
1819
1820
		foreach ($order as $key) {
1821
			if (isset($this->columns[$key])) {
1822
				$new_order[$key] = $this->columns[$key];
1823
			}
1824
		}
1825
1826
		if (sizeof($new_order) === sizeof($this->columns)) {
1827
			$this->columns = $new_order;
1828
		} else {
1829
			throw new DataGridException('When changing columns order, you have to specify all columns');
1830
		}
1831
1832
		return $this;
1833
	}
1834
1835
1836
	/**
1837
	 * Columns order may be different for export and normal grid
1838
	 * @param array $order
1839
	 */
1840
	public function setColumnsExportOrder($order)
1841
	{
1842
		$this->columns_export_order = (array) $order;
1843
	}
1844
1845
1846
	/********************************************************************************
1847
	 *                                SESSION & URL                                 *
1848
	 ********************************************************************************/
1849
1850
1851
	/**
1852
	 * Find some unique session key name
1853
	 * @return string
1854
	 */
1855
	public function getSessionSectionName()
1856
	{
1857
		return $this->getPresenter()->getName().':'.$this->getName();
1858
	}
1859
1860
1861
	/**
1862
	 * Should datagrid remember its filters/pagination/etc using session?
1863
	 * @param bool $remember
1864
	 * @return static
1865
	 */
1866
	public function setRememberState($remember = TRUE)
1867
	{
1868
		$this->remember_state = (bool) $remember;
1869
1870
		return $this;
1871
	}
1872
1873
1874
	/**
1875
	 * Should datagrid refresh url using history API?
1876
	 * @param bool $refresh
1877
	 * @return static
1878
	 */
1879
	public function setRefreshUrl($refresh = TRUE)
1880
	{
1881
		$this->refresh_url = (bool) $refresh;
1882
1883
1884
		return $this;
1885
	}
1886
1887
1888
	/**
1889
	 * Get session data if functionality is enabled
1890
	 * @param  string $key
1891
	 * @return mixed
1892
	 */
1893
	public function getSessionData($key = NULL, $default_value = NULL)
1894
	{
1895
		if (!$this->remember_state) {
1896
			return $key ? $default_value : [];
1897
		}
1898
1899
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
1900
	}
1901
1902
1903
	/**
1904
	 * Save session data - just if it is enabled
1905
	 * @param  string $key
1906
	 * @param  mixed  $value
1907
	 * @return void
1908
	 */
1909
	public function saveSessionData($key, $value)
1910
	{
1911
		if ($this->remember_state) {
1912
			$this->grid_session->{$key} = $value;
1913
		}
1914
	}
1915
1916
1917
	/**
1918
	 * Delete session data
1919
	 * @return void
1920
	 */
1921
	public function deleteSesssionData($key)
1922
	{
1923
		unset($this->grid_session->{$key});
1924
	}
1925
1926
1927
	/********************************************************************************
1928
	 *                                  ITEM DETAIL                                 *
1929
	 ********************************************************************************/
1930
1931
1932
	/**
1933
	 * Get items detail parameters
1934
	 * @return array
1935
	 */
1936
	public function getItemsDetail()
1937
	{
1938
		return $this->items_detail;
1939
	}
1940
1941
1942
	/**
1943
	 * Items can have thair detail - toggled
1944
	 * @param mixed $detail callable|string|bool
1945
	 * @param bool|NULL $primary_where_column
1946
	 * @return Column\ItemDetail
1947
	 */
1948
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1949
	{
1950
		if ($this->isSortable()) {
1951
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1952
		}
1953
1954
		$this->items_detail = new Column\ItemDetail(
1955
			$this,
1956
			$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...
1957
		);
1958
1959
		if (is_string($detail)) {
1960
			/**
1961
			 * Item detail will be in separate template
1962
			 */
1963
			$this->items_detail->setType('template');
1964
			$this->items_detail->setTemplate($detail);
1965
1966
		} else if (is_callable($detail)) {
1967
			/**
1968
			 * Item detail will be rendered via custom callback renderer
1969
			 */
1970
			$this->items_detail->setType('renderer');
1971
			$this->items_detail->setRenderer($detail);
1972
1973
		} else if (TRUE === $detail) {
1974
			/**
1975
			 * Item detail will be rendered probably via block #detail
1976
			 */
1977
			$this->items_detail->setType('block');
1978
1979
		} else {
1980
			throw new DataGridException(
1981
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1982
			);
1983
		}
1984
1985
		return $this->items_detail;
1986
	}
1987
1988
1989
	/********************************************************************************
1990
	 *                                ROW PRIVILEGES                                *
1991
	 ********************************************************************************/
1992
1993
1994
	public function allowRowsGroupAction(callable $condition)
1995
	{
1996
		$this->row_conditions['group_action'] = $condition;
1997
	}
1998
1999
2000
	public function allowRowsAction($key, callable $condition)
2001
	{
2002
		$this->row_conditions['action'][$key] = $condition;
2003
	}
2004
2005
2006
	public function getRowCondition($name, $key = NULL)
2007
	{
2008
		if (!isset($this->row_conditions[$name])) {
2009
			return FALSE;
2010
		}
2011
2012
		$condition = $this->row_conditions[$name];
2013
2014
		if (!$key) {
2015
			return $condition;
2016
		}
2017
2018
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2019
	}
2020
2021
2022
	/********************************************************************************
2023
	 *                               HIDEABLE COLUMNS                               *
2024
	 ********************************************************************************/
2025
2026
2027
	/**
2028
	 * Can datagrid hide colums?
2029
	 * @return boolean
2030
	 */
2031
	public function canHideColumns()
2032
	{
2033
		return (bool) $this->can_hide_columns;
2034
	}
2035
2036
2037
	/**
2038
	 * Order Grid to set columns hideable.
2039
	 * @return static
2040
	 */
2041
	public function setColumnsHideable()
2042
	{
2043
		$this->can_hide_columns = TRUE;
2044
2045
		return $this;
2046
	}
2047
2048
2049
	/********************************************************************************
2050
	 *                                   INTERNAL                                   *
2051
	 ********************************************************************************/
2052
2053
2054
	/**
2055
	 * Get cont of columns
2056
	 * @return int
2057
	 */
2058
	public function getColumnsCount()
2059
	{
2060
		$count = sizeof($this->getColumns());
2061
2062
		if (!empty($this->actions) || $this->isSortable() || $this->getItemsDetail()) {
2063
			$count++;
2064
		}
2065
2066
		if ($this->hasGroupActions()) {
2067
			$count++;
2068
		}
2069
2070
		return $count;
2071
	}
2072
2073
2074
	/**
2075
	 * Get primary key of datagrid data source
2076
	 * @return string
2077
	 */
2078
	public function getPrimaryKey()
2079
	{
2080
		return $this->primary_key;
2081
	}
2082
2083
2084
	/**
2085
	 * Get set of set columns
2086
	 * @return Column\IColumn[]
2087
	 */
2088
	public function getColumns()
2089
	{
2090
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
2091
			$this->removeColumn($column);
2092
		}
2093
2094
		return $this->columns;
2095
	}
2096
2097
2098
	/**
2099
	 * @return PresenterComponent
2100
	 */
2101
	public function getParent()
2102
	{
2103
		$parent = parent::getParent();
2104
2105
		if (!($parent instanceof PresenterComponent)) {
2106
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2107
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2108
			);
2109
		}
2110
2111
		return $parent;
2112
	}
2113
2114
}
2115