Completed
Push — master ( f96f77...348d41 )
by Pavel
02:55
created

DataGrid::findSessionValues()   C

Complexity

Conditions 17
Paths 98

Size

Total Lines 50
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 50
rs 5.3442
cc 17
eloc 26
nc 98
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
367
368
		/**
369
		 * Prepare data for rendering (datagrid may render just one item)
370
		 */
371
		$rows = [];
372
373
		if (!empty($this->redraw_item)) {
374
			$items = $this->dataModel->filterRow($this->redraw_item);
375
		} else {
376
			$items = Nette\Utils\Callback::invokeArgs(
377
				[$this->dataModel, 'filterData'],
378
				[
379
					$this->getPaginator(),
380
					new Sorting($this->sort, $this->sort_callback),
381
					$this->assableFilters()
382
				]
383
			);
384
		}
385
386
		$callback = $this->rowCallback ?: NULL;
387
388
		foreach ($items as $item) {
389
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
390
391
			if ($callback) {
392
				$callback($item, $row->getControl());
393
			}
394
		}
395
396
		if ($this->isTreeView()) {
397
			$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...
398
		}
399
400
		$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...
401
402
		$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...
403
		$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...
404
		$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...
405
		$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...
406
407
		$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...
408
		$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...
409
		$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...
410
		$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...
411
		$this->template->add('columns_visibility', $this->columns_visibility);
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
415
416
		/**
417
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
418
		 */
419
		$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...
420
421
		/**
422
		 * Set template file and render it
423
		 */
424
		$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...
425
		$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...
426
	}
427
428
429
	/********************************************************************************
430
	 *                                 ROW CALLBACK                                 *
431
	 ********************************************************************************/
432
433
434
	/**
435
	 * Each row can be modified with user callback
436
	 * @param  callable  $callback
437
	 * @return static
438
	 */
439
	public function setRowCallback(callable $callback)
440
	{
441
		$this->rowCallback = $callback;
442
443
		return $this;
444
	}
445
446
447
	/********************************************************************************
448
	 *                                 DATA SOURCE                                  *
449
	 ********************************************************************************/
450
451
452
	/**
453
	 * By default ID, you can change that
454
	 * @param string $primary_key
455
	 * @return static
456
	 */
457
	public function setPrimaryKey($primary_key)
458
	{
459
		if ($this->dataModel instanceof DataModel) {
460
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
461
		}
462
463
		$this->primary_key = $primary_key;
464
465
		return $this;
466
	}
467
468
469
	/**
470
	 * Set Grid data source
471
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
472
	 * @return static
473
	 */
474
	public function setDataSource($source)
475
	{
476
		$this->dataModel = new DataModel($source, $this->primary_key);
477
478
		return $this;
479
	}
480
481
482
	/********************************************************************************
483
	 *                                  TEMPLATING                                  *
484
	 ********************************************************************************/
485
486
487
	/**
488
	 * Set custom template file to render
489
	 * @param string $template_file
490
	 * @return static
491
	 */
492
	public function setTemplateFile($template_file)
493
	{
494
		$this->template_file = $template_file;
495
496
		return $this;
497
	}
498
499
500
	/**
501
	 * Get DataGrid template file
502
	 * @return string
503
	 * @return static
504
	 */
505
	public function getTemplateFile()
506
	{
507
		return $this->template_file ?: $this->getOriginalTemplateFile();
508
	}
509
510
511
	/**
512
	 * Get DataGrid original template file
513
	 * @return string
514
	 */
515
	public function getOriginalTemplateFile()
516
	{
517
		return __DIR__.'/templates/datagrid.latte';
518
	}
519
520
521
	/**
522
	 * Tell datagrid wheteher to use or not happy components
523
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
524
	 * @return void|bool
525
	 */
526
	public function useHappyComponents($use = NULL)
527
	{
528
		if (NULL === $use) {
529
			return $this->use_happy_components;
530
		}
531
532
		$this->use_happy_components = (bool) $use;
533
	}
534
535
536
	/********************************************************************************
537
	 *                                   SORTING                                    *
538
	 ********************************************************************************/
539
540
541
	/**
542
	 * Set default sorting
543
	 * @param array $sort
544
	 * @return static
545
	 */
546
	public function setDefaultSort($sort)
547
	{
548
		if (is_string($sort)) {
549
			$sort = [$sort => 'ASC'];
550
		} else {
551
			$sort = (array) $sort;
552
		}
553
554
		$this->default_sort = $sort;
555
556
		return $this;
557
	}
558
559
560
	/**
561
	 * User may set default sorting, apply it
562
	 * @return void
563
	 */
564
	public function findDefaultSort()
565
	{
566
		if (!empty($this->sort)) {
567
			return;
568
		}
569
570
		if (!empty($this->default_sort)) {
571
			$this->sort = $this->default_sort;
572
		}
573
574
		$this->saveSessionData('_grid_sort', $this->sort);
575
	}
576
577
578
	/**
579
	 * Set grido to be sortable
580
	 * @param bool $sortable
581
	 * @return static
582
	 */
583
	public function setSortable($sortable = TRUE)
584
	{
585
		if ($this->getItemsDetail()) {
586
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
587
		}
588
589
		$this->sortable = (bool) $sortable;
590
591
		return $this;
592
	}
593
594
595
	/**
596
	 * Set sortable handle
597
	 * @param string $handler
598
	 * @return static
599
	 */
600
	public function setSortableHandler($handler = 'sort!')
601
	{
602
		$this->sortable_handler = (string) $handler;
603
604
		return $this;
605
	}
606
607
608
	/**
609
	 * Tell whether DataGrid is sortable
610
	 * @return bool
611
	 */
612
	public function isSortable()
613
	{
614
		return $this->sortable;
615
	}
616
617
	/**
618
	 * Return sortable handle name
619
	 * @return string
620
	 */
621
	public function getSortableHandler()
622
	{
623
		return $this->sortable_handler;
624
	}
625
626
627
	/********************************************************************************
628
	 *                                  TREE VIEW                                   *
629
	 ********************************************************************************/
630
631
632
	/**
633
	 * Is tree view set?
634
	 * @return boolean
635
	 */
636
	public function isTreeView()
637
	{
638
		return (bool) $this->tree_view_children_callback;
639
	}
640
641
642
	/**
643
	 * Setting tree view
644
	 * @param callable $get_children_callback
645
	 * @param string|callable $tree_view_has_children_column
646
	 * @return static
647
	 */
648
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
649
	{
650
		if (!is_callable($get_children_callback)) {
651
			throw new DataGridException(
652
				'Parameters to method DataGrid::setTreeView must be of type callable'
653
			);
654
		}
655
656
		if (is_callable($tree_view_has_children_column)) {
657
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
658
			$tree_view_has_children_column = NULL;
659
		}
660
661
		$this->tree_view_children_callback = $get_children_callback;
662
		$this->tree_view_has_children_column = $tree_view_has_children_column;
663
664
		/**
665
		 * TUrn off pagination
666
		 */
667
		$this->setPagination(FALSE);
668
669
		/**
670
		 * Set tree view template file
671
		 */
672
		if (!$this->template_file) {
673
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
674
		}
675
676
		return $this;
677
	}
678
679
680
	/**
681
	 * Is tree view children callback set?
682
	 * @return boolean
683
	 */
684
	public function hasTreeViewChildrenCallback()
685
	{
686
		return is_callable($this->tree_view_has_children_callback);
687
	}
688
689
690
	/**
691
	 * @param  mixed $item
692
	 * @return boolean
693
	 */
694
	public function treeViewChildrenCallback($item)
695
	{
696
		return call_user_func($this->tree_view_has_children_callback, $item);
697
	}
698
699
700
	/********************************************************************************
701
	 *                                    COLUMNS                                   *
702
	 ********************************************************************************/
703
704
705
	/**
706
	 * Add text column with no other formating
707
	 * @param  string      $key
708
	 * @param  string      $name
709
	 * @param  string|null $column
710
	 * @return Column\ColumnText
711
	 */
712
	public function addColumnText($key, $name, $column = NULL)
713
	{
714
		$this->addColumnCheck($key);
715
		$column = $column ?: $key;
716
717
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
718
	}
719
720
721
	/**
722
	 * Add column with link
723
	 * @param  string      $key
724
	 * @param  string      $name
725
	 * @param  string|null $column
726
	 * @return Column\ColumnLink
727
	 */
728
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
729
	{
730
		$this->addColumnCheck($key);
731
		$column = $column ?: $key;
732
		$href = $href ?: $key;
733
734
		if (NULL === $params) {
735
			$params = [$this->primary_key];
736
		}
737
738
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
739
	}
740
741
742
	/**
743
	 * Add column with possible number formating
744
	 * @param  string      $key
745
	 * @param  string      $name
746
	 * @param  string|null $column
747
	 * @return Column\ColumnNumber
748
	 */
749
	public function addColumnNumber($key, $name, $column = NULL)
750
	{
751
		$this->addColumnCheck($key);
752
		$column = $column ?: $key;
753
754
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
755
	}
756
757
758
	/**
759
	 * Add column with date formating
760
	 * @param  string      $key
761
	 * @param  string      $name
762
	 * @param  string|null $column
763
	 * @return Column\ColumnDateTime
764
	 */
765
	public function addColumnDateTime($key, $name, $column = NULL)
766
	{
767
		$this->addColumnCheck($key);
768
		$column = $column ?: $key;
769
770
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
771
	}
772
773
774
	/**
775
	 * Add column status
776
	 * @param  string      $key
777
	 * @param  string      $name
778
	 * @param  string|null $column
779
	 * @return Column\ColumnStatus
780
	 */
781
	public function addColumnStatus($key, $name, $column = NULL)
782
	{
783
		$this->addColumnCheck($key);
784
		$column = $column ?: $key;
785
786
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
787
	}
788
789
790
	/**
791
	 * @param string $key
792
	 * @param Column\Column $column
793
	 * @return Column\Column
794
	 */
795
	protected function addColumn($key, Column\Column $column)
796
	{
797
		$this->onColumnAdd($key, $column);
0 ignored issues
show
Unused Code introduced by
The call to DataGrid::onColumnAdd() has too many arguments starting with $key.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
798
799
		$this->columns_visibility[$key] = [
800
			'visible' => TRUE,
801
			'name' => $column->getName()
802
		];
803
804
		return $this->columns[$key] = $column;
805
	}
806
807
808
	/**
809
	 * Return existing column
810
	 * @param  string $key
811
	 * @return Column\Column
812
	 * @throws DataGridException
813
	 */
814
	public function getColumn($key)
815
	{
816
		if (!isset($this->columns[$key])) {
817
			throw new DataGridException("There is no column at key [$key] defined.");
818
		}
819
820
		return $this->columns[$key];
821
	}
822
823
824
	/**
825
	 * Remove column
826
	 * @param string $key
827
	 * @return void
828
	 */
829
	public function removeColumn($key)
830
	{
831
		unset($this->columns[$key]);
832
	}
833
834
835
	/**
836
	 * Check whether given key already exists in $this->columns
837
	 * @param  string $key
838
	 * @throws DataGridException
839
	 */
840
	protected function addColumnCheck($key)
841
	{
842
		if (isset($this->columns[$key])) {
843
			throw new DataGridException("There is already column at key [$key] defined.");
844
		}
845
	}
846
847
848
	/********************************************************************************
849
	 *                                    ACTIONS                                   *
850
	 ********************************************************************************/
851
852
853
	/**
854
	 * Create action
855
	 * @param string     $key
856
	 * @param string     $name
857
	 * @param string     $href
858
	 * @param array|null $params
859
	 * @return Column\Action
860
	 */
861
	public function addAction($key, $name, $href = NULL, array $params = NULL)
862
	{
863
		$this->addActionCheck($key);
864
		$href = $href ?: $key;
865
866
		if (NULL === $params) {
867
			$params = [$this->primary_key];
868
		}
869
870
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
871
	}
872
873
874
	/**
875
	 * Create action callback
876
	 * @param string     $key
877
	 * @param string     $name
878
	 * @return Column\Action
879
	 */
880
	public function addActionCallback($key, $name, $callback = NULL)
881
	{
882
		$this->addActionCheck($key);
883
		$params = ['__id' => $this->primary_key];
884
885
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
886
887
		if ($callback) {
888
			if (!is_callable($callback)) {
889
				throw new DataGridException('ActionCallback callback has to be callable.');
890
			}
891
892
			$action->onClick[] = $callback;
893
		}
894
895
		return $action;
896
	}
897
898
899
	/**
900
	 * Get existing action
901
	 * @param  string       $key
902
	 * @return Column\Action
903
	 * @throws DataGridException
904
	 */
905
	public function getAction($key)
906
	{
907
		if (!isset($this->actions[$key])) {
908
			throw new DataGridException("There is no action at key [$key] defined.");
909
		}
910
911
		return $this->actions[$key];
912
	}
913
914
915
	/**
916
	 * Remove action
917
	 * @param string $key
918
	 * @return void
919
	 */
920
	public function removeAction($key)
921
	{
922
		unset($this->actions[$key]);
923
	}
924
925
926
	/**
927
	 * Check whether given key already exists in $this->filters
928
	 * @param  string $key
929
	 * @throws DataGridException
930
	 */
931
	protected function addActionCheck($key)
932
	{
933
		if (isset($this->actions[$key])) {
934
			throw new DataGridException("There is already action at key [$key] defined.");
935
		}
936
	}
937
938
939
	/********************************************************************************
940
	 *                                    FILTERS                                   *
941
	 ********************************************************************************/
942
943
944
	/**
945
	 * Add filter fot text search
946
	 * @param string       $key
947
	 * @param string       $name
948
	 * @param array|string $columns
949
	 * @return Filter\FilterText
950
	 * @throws DataGridException
951
	 */
952
	public function addFilterText($key, $name, $columns = NULL)
953
	{
954
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
955
956
		if (!is_array($columns)) {
957
			throw new DataGridException("Filter Text can except only array or string.");
958
		}
959
960
		$this->addFilterCheck($key);
961
962
		return $this->filters[$key] = new Filter\FilterText($key, $name, $columns);
963
	}
964
965
966
	/**
967
	 * Add select box filter
968
	 * @param string $key
969
	 * @param string $name
970
	 * @param array  $options
971
	 * @param string $column
972
	 * @return Filter\FilterSelect
973
	 * @throws DataGridException
974
	 */
975 View Code Duplication
	public function addFilterSelect($key, $name, array $options, $column = NULL)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
976
	{
977
		$column = $column ?: $key;
978
979
		if (!is_string($column)) {
980
			throw new DataGridException("Filter Select can only filter in one column.");
981
		}
982
983
		$this->addFilterCheck($key);
984
985
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
986
	}
987
988
989
	/**
990
	 * Add multi select box filter
991
	 * @param string $key
992
	 * @param string $name
993
	 * @param array  $options
994
	 * @param string $column
995
	 * @return Filter\FilterSelect
996
	 * @throws DataGridException
997
	 */
998
	public function addFilterMultiSelect($key, $name, array $options, $column = NULL)
999
	{
1000
		$column = $column ?: $key;
1001
1002
		if (!is_string($column)) {
1003
			throw new DataGridException("Filter MultiSelect can only filter in one column.");
1004
		}
1005
1006
		$this->addFilterCheck($key);
1007
1008
		return $this->filters[$key] = new Filter\FilterMultiSelect($key, $name, $options, $column);
1009
	}
1010
1011
1012
	/**
1013
	 * Add datepicker filter
1014
	 * @param string $key
1015
	 * @param string $name
1016
	 * @param string $column
1017
	 * @return Filter\FilterDate
1018
	 * @throws DataGridException
1019
	 */
1020
	public function addFilterDate($key, $name, $column = NULL)
1021
	{
1022
		$column = $column ?: $key;
1023
1024
		if (!is_string($column)) {
1025
			throw new DataGridException("FilterDate can only filter in one column.");
1026
		}
1027
1028
		$this->addFilterCheck($key);
1029
1030
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
1031
	}
1032
1033
1034
	/**
1035
	 * Add range filter (from - to)
1036
	 * @param string $key
1037
	 * @param string $name
1038
	 * @param string $column
1039
	 * @return Filter\FilterRange
1040
	 * @throws DataGridException
1041
	 */
1042 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...
1043
	{
1044
		$column = $column ?: $key;
1045
1046
		if (!is_string($column)) {
1047
			throw new DataGridException("FilterRange can only filter in one column.");
1048
		}
1049
1050
		$this->addFilterCheck($key);
1051
1052
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
1053
	}
1054
1055
1056
	/**
1057
	 * Add datepicker filter (from - to)
1058
	 * @param string $key
1059
	 * @param string $name
1060
	 * @param string $column
1061
	 * @return Filter\FilterDateRange
1062
	 * @throws DataGridException
1063
	 */
1064 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...
1065
	{
1066
		$column = $column ?: $key;
1067
1068
		if (!is_string($column)) {
1069
			throw new DataGridException("FilterDateRange can only filter in one column.");
1070
		}
1071
1072
		$this->addFilterCheck($key);
1073
1074
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
1075
	}
1076
1077
1078
	/**
1079
	 * Check whether given key already exists in $this->filters
1080
	 * @param  string $key
1081
	 * @throws DataGridException
1082
	 */
1083
	protected function addFilterCheck($key)
1084
	{
1085
		if (isset($this->filters[$key])) {
1086
			throw new DataGridException("There is already action at key [$key] defined.");
1087
		}
1088
	}
1089
1090
1091
	/**
1092
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1093
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1094
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1095
	 */
1096
	public function assableFilters()
1097
	{
1098
		foreach ($this->filter as $key => $value) {
1099
			if (!isset($this->filters[$key])) {
1100
				$this->deleteSesssionData($key);
1101
1102
				continue;
1103
			}
1104
1105
			if (is_array($value) || $value instanceof \Traversable) {
1106
				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...
1107
					$this->filters[$key]->setValue($value);
1108
				}
1109
			} else {
1110
				if ($value !== '' && $value !== NULL) {
1111
					$this->filters[$key]->setValue($value);
1112
				}
1113
			}
1114
		}
1115
1116
		foreach ($this->columns as $column) {
1117
			if (isset($this->sort[$column->getSortingColumn()])) {
1118
				$column->setSort($this->sort);
1119
			}
1120
		}
1121
1122
		return $this->filters;
1123
	}
1124
1125
1126
	/**
1127
	 * Remove filter
1128
	 * @param string $key
1129
	 * @return void
1130
	 */
1131
	public function removeFilter($key)
1132
	{
1133
		unset($this->filters[$key]);
1134
	}
1135
1136
1137
	/**
1138
	 * Get defined filter
1139
	 * @param  string $key
1140
	 * @return Filter\Filter
1141
	 */
1142
	public function getFilter($key)
1143
	{
1144
		if (!isset($this->filters[$key])) {
1145
			throw new DataGridException("Filter [{$key}] is not defined");
1146
		}
1147
1148
		return $this->filters[$key];
1149
	}
1150
1151
1152
	/********************************************************************************
1153
	 *                                  FILTERING                                   *
1154
	 ********************************************************************************/
1155
1156
1157
	/**
1158
	 * Is filter active?
1159
	 * @return boolean
1160
	 */
1161
	public function isFilterActive()
1162
	{
1163
		$is_filter = ArraysHelper::testTruthy($this->filter);
1164
1165
		return ($is_filter) || $this->force_filter_active;
1166
	}
1167
1168
1169
	/**
1170
	 * Tell that filter is active from whatever reasons
1171
	 * return static
1172
	 */
1173
	public function setFilterActive()
1174
	{
1175
		$this->force_filter_active = TRUE;
1176
1177
		return $this;
1178
	}
1179
1180
1181
	/**
1182
	 * Set filter values (force - overwrite user data)
1183
	 * @param array $filter
1184
	 * @return static
1185
	 */
1186
	public function setFilter(array $filter)
1187
	{
1188
		$this->filter = $filter;
1189
1190
		$this->saveSessionData('_grid_has_filtered', 1);
1191
1192
		return $this;
1193
	}
1194
1195
1196
	/**
1197
	 * If we want to sent some initial filter
1198
	 * @param array $filter
0 ignored issues
show
Documentation introduced by
There is no parameter named $filter. Did you maybe mean $default_filter?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1199
	 * @param bool  $use_on_reset
1200
	 * @return static
1201
	 */
1202
	public function setDefaultFilter(array $default_filter, $use_on_reset = TRUE)
1203
	{
1204
		foreach ($default_filter as $key => $value) {
1205
			$filter = $this->getFilter($key);
1206
1207
			if (!$filter) {
1208
				throw new DataGridException("Can not set default value to nonexisting filter [$key]");
1209
			}
1210
1211
			if ($filter instanceof Filter\FilterMultiSelect && !is_array($value)) {
1212
				throw new DataGridException(
1213
					"Default value of filter [$key] - MultiSelect has to be an array"
1214
				);
1215
			}
1216
1217
			if ($filter instanceof Filter\FilterRange || $filter instanceof Filter\FilterDateRange) {
1218
				if (!is_array($value)) {
1219
					throw new DataGridException(
1220
						"Default value of filter [$key] - Range/DateRange has to be an array [from/to => ...]"
1221
					);
1222
				}
1223
1224
				$temp = $value;
1225
				unset($temp['from'], $temp['to']);
1226
1227
				if (!empty($temp)) {
1228
					throw new DataGridException(
1229
						"Default value of filter [$key] - Range/DateRange can contain only [from/to => ...] values"
1230
					);
1231
				}
1232
			}
1233
		}
1234
1235
		$this->default_filter = $default_filter;
1236
		$this->default_filter_use_on_reset = (bool) $use_on_reset;
1237
1238
		return $this;
1239
	}
1240
1241
1242
	/**
1243
	 * User may set default filter, find it
1244
	 * @return void
1245
	 */
1246
	public function findDefaultFilter()
1247
	{
1248
		if (!empty($this->filter)) {
1249
			return;
1250
		}
1251
1252
		if ($this->getSessionData('_grid_has_filtered')) {
1253
			return;
1254
		}
1255
1256
		if (!empty($this->default_filter)) {
1257
			$this->filter = $this->default_filter;
1258
		}
1259
1260
		foreach ($this->filter as $key => $value) {
1261
			$this->saveSessionData($key, $value);
1262
		}
1263
	}
1264
1265
1266
	/**
1267
	 * FilterAndGroupAction form factory
1268
	 * @return Form
1269
	 */
1270
	public function createComponentFilter()
1271
	{
1272
		$form = new Form($this, 'filter');
1273
1274
		$form->setMethod('get');
1275
1276
		$form->setTranslator($this->getTranslator());
1277
1278
		/**
1279
		 * InlineEdit part
1280
		 */
1281
		$inline_edit_container = $form->addContainer('inline_edit');
1282
1283 View Code Duplication
		if ($this->inlineEdit instanceof InlineEdit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1284
			$inline_edit_container->addSubmit('submit', 'ublaboo_datagrid.save');
1285
			$inline_edit_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1286
				->setValidationScope(FALSE);
1287
1288
			$this->inlineEdit->onControlAdd($inline_edit_container);
1289
		}
1290
1291
		/**
1292
		 * InlineAdd part
1293
		 */
1294
		$inline_add_container = $form->addContainer('inline_add');
1295
1296 View Code Duplication
		if ($this->inlineAdd instanceof InlineEdit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1297
			$inline_add_container->addSubmit('submit', 'ublaboo_datagrid.save');
1298
			$inline_add_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1299
				->setValidationScope(FALSE)
1300
				->setAttribute('data-datagrid-cancel-inline-add', TRUE);
1301
1302
			$this->inlineAdd->onControlAdd($inline_add_container);
1303
		}
1304
1305
		/**
1306
		 * ItemDetail form part
1307
		 */
1308
		$items_detail_form = $this->getItemDetailForm();
1309
1310
		if ($items_detail_form instanceof Nette\Forms\Container) {
1311
			$form['items_detail_form'] = $items_detail_form;
1312
		}
1313
1314
		/**
1315
		 * Filter part
1316
		 */
1317
		$filter_container = $form->addContainer('filter');
1318
1319
		foreach ($this->filters as $filter) {
1320
			$filter->addToFormContainer($filter_container);
1321
		}
1322
1323
		/**
1324
		 * Group action part
1325
		 */
1326
		$group_action_container = $form->addContainer('group_action');
1327
1328
		if ($this->hasGroupActions()) {
1329
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1330
		}
1331
1332
		$form->setDefaults(['filter' => $this->filter]);
1333
1334
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1335
1336
		return $form;
1337
	}
1338
1339
1340
	/**
1341
	 * Set $this->filter values after filter form submitted
1342
	 * @param  Form $form
1343
	 * @return void
1344
	 */
1345
	public function filterSucceeded(Form $form)
1346
	{
1347
		if ($this->snippets_set) {
1348
			return;
1349
		}
1350
1351
		$values = $form->getValues();
1352
1353
		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...
1354
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1355
				return;
1356
			}
1357
		}
1358
1359
		/**
1360
		 * Inline edit
1361
		 */
1362
		$inline_edit = $form['inline_edit'];
1363
1364
		if (isset($inline_edit) && isset($inline_edit['submit']) && isset($inline_edit['cancel'])) {
1365
			if ($inline_edit['submit']->isSubmittedBy() || $inline_edit['cancel']->isSubmittedBy()) {
1366
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1367
				$primary_where_column = $form->getHttpData(
1368
					Form::DATA_LINE,
1369
					'inline_edit[_primary_where_column]'
1370
				);
1371
1372 View Code Duplication
				if ($inline_edit['submit']->isSubmittedBy()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1373
					$this->inlineEdit->onSubmit($id, $values->inline_edit);
1374
1375
					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...
1376
						$this->getPresenter()->payload->_datagrid_inline_edited = $id;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1377
					}
1378
				}
1379
1380
				$this->redrawItem($id, $primary_where_column);
1381
1382
				return;
1383
			}
1384
		}
1385
1386
		/**
1387
		 * Inline add
1388
		 */
1389
		$inline_add = $form['inline_add'];
1390
1391
		if (isset($inline_add) && isset($inline_add['submit']) && isset($inline_add['cancel'])) {
1392
			if ($inline_add['submit']->isSubmittedBy() || $inline_add['cancel']->isSubmittedBy()) {
1393 View Code Duplication
				if ($inline_add['submit']->isSubmittedBy()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1394
					$this->inlineAdd->onSubmit($values->inline_add);
0 ignored issues
show
Bug introduced by
The call to onSubmit() misses a required argument $values.

This check looks for function calls that miss required arguments.

Loading history...
1395
1396
					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...
1397
						$this->getPresenter()->payload->_datagrid_inline_added = TRUE;
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1398
					}
1399
				}
1400
1401
				return;
1402
			}
1403
		}
1404
1405
		/**
1406
		 * Filter itself
1407
		 */
1408
		$values = $values['filter'];
1409
1410
		foreach ($values as $key => $value) {
1411
			/**
1412
			 * Session stuff
1413
			 */
1414
			$this->saveSessionData($key, $value);
1415
1416
			/**
1417
			 * Other stuff
1418
			 */
1419
			$this->filter[$key] = $value;
1420
		}
1421
1422
		if (!empty($values)) {
1423
			$this->saveSessionData('_grid_has_filtered', 1);
1424
		}
1425
1426
		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...
1427
			$this->getPresenter()->payload->_datagrid_sort = [];
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1428
1429
			foreach ($this->columns as $key => $column) {
1430
				if ($column->isSortable()) {
1431
					$this->getPresenter()->payload->_datagrid_sort[$key] = $this->link('sort!', [
0 ignored issues
show
Bug introduced by
Accessing payload on the interface Nette\ComponentModel\IComponent suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1432
						'sort' => $column->getSortNext()
1433
					]);
1434
				}
1435
			}
1436
		}
1437
1438
		$this->reload();
1439
	}
1440
1441
1442
	/**
1443
	 * Should be datagrid filters rendered separately?
1444
	 * @param boolean $out
1445
	 * @return static
1446
	 */
1447
	public function setOuterFilterRendering($out = TRUE)
1448
	{
1449
		$this->outer_filter_rendering = (bool) $out;
1450
1451
		return $this;
1452
	}
1453
1454
1455
	/**
1456
	 * Are datagrid filters rendered separately?
1457
	 * @return boolean
1458
	 */
1459
	public function hasOuterFilterRendering()
1460
	{
1461
		return $this->outer_filter_rendering;
1462
	}
1463
1464
1465
	/**
1466
	 * Try to restore session stuff
1467
	 * @return void
1468
	 */
1469
	public function findSessionValues()
1470
	{
1471
		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...
1472
			return;
1473
		}
1474
1475
		if (!$this->remember_state) {
1476
			return;
1477
		}
1478
1479
		if ($page = $this->getSessionData('_grid_page')) {
1480
			$this->page = $page;
1481
		}
1482
1483
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1484
			$this->per_page = $per_page;
1485
		}
1486
1487
		if ($sort = $this->getSessionData('_grid_sort')) {
1488
			$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...
1489
		}
1490
1491
		foreach ($this->getSessionData() as $key => $value) {
1492
			$other_session_keys = [
1493
				'_grid_per_page',
1494
				'_grid_sort',
1495
				'_grid_page',
1496
				'_grid_has_filtered',
1497
				'_grid_hidden_columns',
1498
				'_grid_hidden_columns_manipulated'
1499
			];
1500
1501
			if (!in_array($key, $other_session_keys)) {
1502
				$this->filter[$key] = $value;
1503
			}
1504
		}
1505
1506
		/**
1507
		 * When column is sorted via custom callback, apply it
1508
		 */
1509
		if (empty($this->sort_callback) && !empty($this->sort)) {
1510
			foreach ($this->sort as $key => $order) {
1511
				$column = $this->getColumn($key);
1512
1513
				if ($column && $column->isSortable() && is_callable($column->getSortableCallback())) {
1514
					$this->sort_callback = $column->getSortableCallback();
1515
				}
1516
			}
1517
		}
1518
	}
1519
1520
1521
	/********************************************************************************
1522
	 *                                    EXPORTS                                   *
1523
	 ********************************************************************************/
1524
1525
1526
	/**
1527
	 * Add export of type callback
1528
	 * @param string $text
1529
	 * @param callable $callback
1530
	 * @param boolean $filtered
1531
	 * @return Export\Export
1532
	 */
1533
	public function addExportCallback($text, $callback, $filtered = FALSE)
1534
	{
1535
		if (!is_callable($callback)) {
1536
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1537
		}
1538
1539
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1540
	}
1541
1542
1543
	/**
1544
	 * Add already implemented csv export
1545
	 * @param string      $text
1546
	 * @param string      $csv_file_name
1547
	 * @param string|null $output_encoding
1548
	 * @param string|null $delimiter
1549
	 * @return Export\Export
1550
	 */
1551
	public function addExportCsv($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL)
1552
	{
1553
		return $this->addToExports(new Export\ExportCsv(
1554
			$text,
1555
			$csv_file_name,
1556
			FALSE,
1557
			$output_encoding,
1558
			$delimiter
1559
		));
1560
	}
1561
1562
1563
	/**
1564
	 * Add already implemented csv export, but for filtered data
1565
	 * @param string      $text
1566
	 * @param string      $csv_file_name
1567
	 * @param string|null $output_encoding
1568
	 * @param string|null $delimiter
1569
	 * @return Export\Export
1570
	 */
1571
	public function addExportCsvFiltered($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL)
1572
	{
1573
		return $this->addToExports(new Export\ExportCsv(
1574
			$text,
1575
			$csv_file_name,
1576
			TRUE,
1577
			$output_encoding,
1578
			$delimiter
1579
		));
1580
	}
1581
1582
1583
	/**
1584
	 * Add export to array
1585
	 * @param Export\Export $export
1586
	 * @return Export\Export
1587
	 */
1588
	protected function addToExports(Export\Export $export)
1589
	{
1590
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1591
1592
		$export->setLink($this->link('export!', ['id' => $id]));
1593
1594
		return $this->exports[$id] = $export;
1595
	}
1596
1597
1598
	public function resetExportsLinks()
1599
	{
1600
		foreach ($this->exports as $id => $export) {
1601
			$export->setLink($this->link('export!', ['id' => $id]));
1602
		}
1603
	}
1604
1605
1606
	/********************************************************************************
1607
	 *                                 GROUP ACTIONS                                *
1608
	 ********************************************************************************/
1609
1610
1611
	/**
1612
	 * Alias for add group select action
1613
	 * @param string $title
1614
	 * @param array  $options
1615
	 * @return GroupAction\GroupAction
1616
	 */
1617
	public function addGroupAction($title, $options = [])
1618
	{
1619
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1620
	}
1621
1622
	/**
1623
	 * Add group action (select box)
1624
	 * @param string $title
1625
	 * @param array  $options
1626
	 * @return GroupAction\GroupAction
1627
	 */
1628
	public function addGroupSelectAction($title, $options = [])
1629
	{
1630
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
1631
	}
1632
1633
	/**
1634
	 * Add group action (text input)
1635
	 * @param string $title
1636
	 * @return GroupAction\GroupAction
1637
	 */
1638
	public function addGroupTextAction($title)
1639
	{
1640
		return $this->getGroupActionCollection()->addGroupTextAction($title);
1641
	}
1642
1643
	/**
1644
	 * Get collection of all group actions
1645
	 * @return GroupAction\GroupActionCollection
1646
	 */
1647
	public function getGroupActionCollection()
1648
	{
1649
		if (!$this->group_action_collection) {
1650
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1651
		}
1652
1653
		return $this->group_action_collection;
1654
	}
1655
1656
1657
	/**
1658
	 * Has datagrid some group actions?
1659
	 * @return boolean
1660
	 */
1661
	public function hasGroupActions()
1662
	{
1663
		return (bool) $this->group_action_collection;
1664
	}
1665
1666
1667
	/********************************************************************************
1668
	 *                                   HANDLERS                                   *
1669
	 ********************************************************************************/
1670
1671
1672
	/**
1673
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1674
	 * @param  int  $page
1675
	 * @return void
1676
	 */
1677
	public function handlePage($page)
1678
	{
1679
		/**
1680
		 * Session stuff
1681
		 */
1682
		$this->page = $page;
1683
		$this->saveSessionData('_grid_page', $page);
1684
1685
		$this->reload(['table']);
1686
	}
1687
1688
1689
	/**
1690
	 * Handler for sorting
1691
	 * @param array $sort
1692
	 * @return void
1693
	 */
1694
	public function handleSort(array $sort)
1695
	{
1696
		$new_sort = [];
1697
1698
		/**
1699
		 * Find apropirate column
1700
		 */
1701
		foreach ($sort as $key => $value) {
1702
			if (empty($this->columns[$key])) {
1703
				throw new DataGridException("Column <$key> not found");
1704
			}
1705
1706
			$column = $this->columns[$key];
1707
			$new_sort = [$column->getSortingColumn() => $value];
1708
1709
			/**
1710
			 * Pagination may be reseted after sorting
1711
			 */
1712
			if ($column->sortableResetPagination()) {
1713
				$this->page = 1;
1714
				$this->saveSessionData('_grid_page', 1);
1715
			}
1716
1717
			/**
1718
			 * Custom sorting callback may be applied
1719
			 */
1720
			if ($column->getSortableCallback()) {
1721
				$this->sort_callback = $column->getSortableCallback();
1722
			}
1723
		}
1724
1725
		/**
1726
		 * Session stuff
1727
		 */
1728
		$this->sort = $new_sort;
1729
		$this->saveSessionData('_grid_sort', $this->sort);
1730
1731
		$this->reload(['table']);
1732
	}
1733
1734
1735
	/**
1736
	 * handler for reseting the filter
1737
	 * @return void
1738
	 */
1739
	public function handleResetFilter()
1740
	{
1741
		/**
1742
		 * Session stuff
1743
		 */
1744
		$this->deleteSesssionData('_grid_page');
1745
1746
		if ($this->default_filter_use_on_reset) {
1747
			$this->deleteSesssionData('_grid_has_filtered');
1748
		}
1749
1750
		foreach ($this->getSessionData() as $key => $value) {
1751
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_has_filtered'])) {
1752
				$this->deleteSesssionData($key);
1753
			}
1754
		}
1755
1756
		$this->filter = [];
1757
1758
		$this->reload(['grid']);
1759
	}
1760
1761
1762
	/**
1763
	 * Handler for export
1764
	 * @param  int $id Key for particular export class in array $this->exports
1765
	 * @return void
1766
	 */
1767
	public function handleExport($id)
1768
	{
1769
		if (!isset($this->exports[$id])) {
1770
			throw new Nette\Application\ForbiddenRequestException;
1771
		}
1772
1773
		if (!empty($this->columns_export_order)) {
1774
			$this->setColumnsOrder($this->columns_export_order);
1775
		}
1776
1777
		$export = $this->exports[$id];
1778
1779
		if ($export->isFiltered()) {
1780
			$sort      = $this->sort;
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1781
			$filter    = $this->assableFilters();
1782
		} else {
1783
			$sort      = [$this->primary_key => 'ASC'];
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1784
			$filter    = [];
1785
		}
1786
1787
		if (NULL === $this->dataModel) {
1788
			throw new DataGridException('You have to set a data source first.');
1789
		}
1790
1791
		$rows = [];
1792
1793
		$items = Nette\Utils\Callback::invokeArgs(
1794
			[$this->dataModel, 'filterData'], [
1795
				NULL,
1796
				new Sorting($this->sort, $this->sort_callback),
1797
				$filter
1798
			]
1799
		);
1800
1801
		foreach ($items as $item) {
1802
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1803
		}
1804
1805
		if ($export instanceof Export\ExportCsv) {
1806
			$export->invoke($rows, $this);
1807
		} else {
1808
			$export->invoke($items, $this);
1809
		}
1810
1811
		if ($export->isAjax()) {
1812
			$this->reload();
1813
		}
1814
	}
1815
1816
1817
	/**
1818
	 * Handler for getting children of parent item (e.g. category)
1819
	 * @param  int $parent
1820
	 * @return void
1821
	 */
1822 View Code Duplication
	public function handleGetChildren($parent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1823
	{
1824
		$this->setDataSource(
1825
			call_user_func($this->tree_view_children_callback, $parent)
1826
		);
1827
1828
		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...
1829
			$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...
1830
			$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...
1831
1832
			$this->redrawControl('items');
1833
1834
			$this->onRedraw();
1835
		} else {
1836
			$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...
1837
		}
1838
	}
1839
1840
1841
	/**
1842
	 * Handler for getting item detail
1843
	 * @param  mixed $id
1844
	 * @return void
1845
	 */
1846 View Code Duplication
	public function handleGetItemDetail($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1847
	{
1848
		$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...
1849
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1850
1851
		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...
1852
			$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...
1853
			$this->redrawControl('items');
1854
1855
			$this->onRedraw();
1856
		} else {
1857
			$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...
1858
		}
1859
	}
1860
1861
1862
	/**
1863
	 * Handler for inline editing
1864
	 * @param  mixed $id
1865
	 * @param  mixed $key
1866
	 * @return void
1867
	 */
1868
	public function handleEdit($id, $key)
1869
	{
1870
		$column = $this->getColumn($key);
1871
		$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...
1872
1873
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1874
	}
1875
1876
1877
	/**
1878
	 * Redraw $this
1879
	 * @return void
1880
	 */
1881
	public function reload($snippets = [])
1882
	{
1883
		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...
1884
			$this->redrawControl('tbody');
1885
			$this->redrawControl('pagination');
1886
1887
			/**
1888
			 * manualy reset exports links...
1889
			 */
1890
			$this->resetExportsLinks();
1891
			$this->redrawControl('exports');
1892
1893
			foreach ($snippets as $snippet) {
1894
				$this->redrawControl($snippet);
1895
			}
1896
1897
			$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...
1898
1899
			$this->onRedraw();
1900
		} else {
1901
			$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...
1902
		}
1903
	}
1904
1905
1906
	/**
1907
	 * Handler for column status
1908
	 * @param  string $id
1909
	 * @param  string $key
1910
	 * @param  string $value
1911
	 * @return void
1912
	 */
1913
	public function handleChangeStatus($id, $key, $value)
1914
	{
1915
		if (empty($this->columns[$key])) {
1916
			throw new DataGridException("ColumnStatus[$key] does not exist");
1917
		}
1918
1919
		$this->columns[$key]->onChange($id, $value);
1920
	}
1921
1922
1923
	/**
1924
	 * Redraw just one row via ajax
1925
	 * @param  int   $id
1926
	 * @param  mixed $primary_where_column
1927
	 * @return void
1928
	 */
1929
	public function redrawItem($id, $primary_where_column = NULL)
1930
	{
1931
		$this->snippets_set = TRUE;
1932
1933
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1934
1935
		$this->redrawControl('items');
1936
		$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...
1937
1938
		$this->onRedraw();
1939
	}
1940
1941
1942
	/**
1943
	 * Tell datagrid to display all columns
1944
	 * @return void
1945
	 */
1946
	public function handleShowAllColumns()
1947
	{
1948
		$this->deleteSesssionData('_grid_hidden_columns');
1949
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
1950
1951
		$this->redrawControl();
1952
1953
		$this->onRedraw();
1954
	}
1955
1956
1957
	/**
1958
	 * Tell datagrid to display default columns
1959
	 * @return void
1960
	 */
1961
	public function handleShowDefaultColumns()
1962
	{
1963
		$this->deleteSesssionData('_grid_hidden_columns');
1964
		$this->saveSessionData('_grid_hidden_columns_manipulated', FALSE);
1965
1966
		$this->redrawControl();
1967
1968
		$this->onRedraw();
1969
	}
1970
1971
1972
	/**
1973
	 * Reveal particular column
1974
	 * @param  string $column
1975
	 * @return void
1976
	 */
1977 View Code Duplication
	public function handleShowColumn($column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1978
	{
1979
		$columns = $this->getSessionData('_grid_hidden_columns');
1980
1981
		if (!empty($columns)) {
1982
			$pos = array_search($column, $columns);
1983
1984
			if ($pos !== FALSE) {
1985
				unset($columns[$pos]);
1986
			}
1987
		}
1988
1989
		$this->saveSessionData('_grid_hidden_columns', $columns);
1990
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
1991
1992
		$this->redrawControl();
1993
1994
		$this->onRedraw();
1995
	}
1996
1997
1998
	/**
1999
	 * Notice datagrid to not display particular columns
2000
	 * @param  string $column
2001
	 * @return void
2002
	 */
2003 View Code Duplication
	public function handleHideColumn($column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2004
	{
2005
		/**
2006
		 * Store info about hiding a column to session
2007
		 */
2008
		$columns = $this->getSessionData('_grid_hidden_columns');
2009
2010
		if (empty($columns)) {
2011
			$columns = [$column];
2012
		} else if (!in_array($column, $columns)) {
2013
			array_push($columns, $column);
2014
		}
2015
2016
		$this->saveSessionData('_grid_hidden_columns', $columns);
2017
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2018
2019
		$this->redrawControl();
2020
2021
		$this->onRedraw();
2022
	}
2023
2024
2025
	public function handleActionCallback($__key, $__id)
2026
	{
2027
		$action = $this->getAction($__key);
2028
2029
		if (!($action instanceof Column\ActionCallback)) {
2030
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
2031
		}
2032
2033
		$action->onClick($__id);
2034
	}
2035
2036
2037
	/********************************************************************************
2038
	 *                                  PAGINATION                                  *
2039
	 ********************************************************************************/
2040
2041
2042
	/**
2043
	 * Set options of select "items_per_page"
2044
	 * @param array $items_per_page_list
2045
	 * @return static
2046
	 */
2047
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
2048
	{
2049
		$this->items_per_page_list = $items_per_page_list;
2050
2051
		if ($include_all) {
2052
			$this->items_per_page_list[] = 'all';
2053
		}
2054
2055
		return $this;
2056
	}
2057
2058
2059
	/**
2060
	 * Paginator factory
2061
	 * @return Components\DataGridPaginator\DataGridPaginator
2062
	 */
2063
	public function createComponentPaginator()
2064
	{
2065
		/**
2066
		 * Init paginator
2067
		 */
2068
		$component = new Components\DataGridPaginator\DataGridPaginator(
2069
			$this->getTranslator()
2070
		);
2071
		$paginator = $component->getPaginator();
2072
2073
		$paginator->setPage($this->page);
2074
		$paginator->setItemsPerPage($this->getPerPage());
2075
2076
		return $component;
2077
	}
2078
2079
2080
	/**
2081
	 * PerPage form factory
2082
	 * @return Form
2083
	 */
2084
	public function createComponentPerPage()
2085
	{
2086
		$form = new Form;
2087
2088
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
2089
			->setValue($this->getPerPage());
2090
2091
		$form->addSubmit('submit', '');
2092
2093
		$saveSessionData = [$this, 'saveSessionData'];
2094
2095
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
2096
			/**
2097
			 * Session stuff
2098
			 */
2099
			$saveSessionData('_grid_per_page', $values->per_page);
2100
2101
			/**
2102
			 * Other stuff
2103
			 */
2104
			$this->per_page = $values->per_page;
2105
			$this->reload();
2106
		};
2107
2108
		return $form;
2109
	}
2110
2111
2112
	/**
2113
	 * Get parameter per_page
2114
	 * @return int
2115
	 */
2116
	public function getPerPage()
2117
	{
2118
		$items_per_page_list = $this->getItemsPerPageList();
2119
2120
		$per_page = $this->per_page ?: reset($items_per_page_list);
2121
2122
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
2123
			$per_page = reset($items_per_page_list);
2124
		}
2125
2126
		return $per_page;
2127
	}
2128
2129
2130
	/**
2131
	 * Get associative array of items_per_page_list
2132
	 * @return array
2133
	 */
2134
	public function getItemsPerPageList()
2135
	{
2136
		if (empty($this->items_per_page_list)) {
2137
			$this->setItemsPerPageList([10, 20, 50], TRUE);
2138
		}
2139
2140
		$list = array_flip($this->items_per_page_list);
2141
2142
		foreach ($list as $key => $value) {
2143
			$list[$key] = $key;
2144
		}
2145
2146
		if (array_key_exists('all', $list)) {
2147
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
2148
		}
2149
2150
		return $list;
2151
	}
2152
2153
2154
	/**
2155
	 * Order Grid to "be paginated"
2156
	 * @param bool $do
2157
	 * @return static
2158
	 */
2159
	public function setPagination($do)
2160
	{
2161
		$this->do_paginate = (bool) $do;
2162
2163
		return $this;
2164
	}
2165
2166
2167
	/**
2168
	 * Tell whether Grid is paginated
2169
	 * @return bool
2170
	 */
2171
	public function isPaginated()
2172
	{
2173
		return $this->do_paginate;
2174
	}
2175
2176
2177
	/**
2178
	 * Return current paginator class
2179
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
2180
	 */
2181
	public function getPaginator()
2182
	{
2183
		if ($this->isPaginated() && $this->per_page !== 'all') {
2184
			return $this['paginator'];
2185
		}
2186
2187
		return NULL;
2188
	}
2189
2190
2191
	/********************************************************************************
2192
	 *                                     I18N                                     *
2193
	 ********************************************************************************/
2194
2195
2196
	/**
2197
	 * Set datagrid translator
2198
	 * @param Nette\Localization\ITranslator $translator
2199
	 * @return static
2200
	 */
2201
	public function setTranslator(Nette\Localization\ITranslator $translator)
2202
	{
2203
		$this->translator = $translator;
2204
2205
		return $this;
2206
	}
2207
2208
2209
	/**
2210
	 * Get translator for datagrid
2211
	 * @return Nette\Localization\ITranslator
2212
	 */
2213
	public function getTranslator()
2214
	{
2215
		if (!$this->translator) {
2216
			$this->translator = new Localization\SimpleTranslator;
2217
		}
2218
2219
		return $this->translator;
2220
	}
2221
2222
2223
	/********************************************************************************
2224
	 *                                 COLUMNS ORDER                                *
2225
	 ********************************************************************************/
2226
2227
2228
	/**
2229
	 * Set order of datagrid columns
2230
	 * @param array $order
2231
	 * @return static
2232
	 */
2233
	public function setColumnsOrder($order)
2234
	{
2235
		$new_order = [];
2236
2237
		foreach ($order as $key) {
2238
			if (isset($this->columns[$key])) {
2239
				$new_order[$key] = $this->columns[$key];
2240
			}
2241
		}
2242
2243
		if (sizeof($new_order) === sizeof($this->columns)) {
2244
			$this->columns = $new_order;
2245
		} else {
2246
			throw new DataGridException('When changing columns order, you have to specify all columns');
2247
		}
2248
2249
		return $this;
2250
	}
2251
2252
2253
	/**
2254
	 * Columns order may be different for export and normal grid
2255
	 * @param array $order
2256
	 */
2257
	public function setColumnsExportOrder($order)
2258
	{
2259
		$this->columns_export_order = (array) $order;
2260
	}
2261
2262
2263
	/********************************************************************************
2264
	 *                                SESSION & URL                                 *
2265
	 ********************************************************************************/
2266
2267
2268
	/**
2269
	 * Find some unique session key name
2270
	 * @return string
2271
	 */
2272
	public function getSessionSectionName()
2273
	{
2274
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
2275
	}
2276
2277
2278
	/**
2279
	 * Should datagrid remember its filters/pagination/etc using session?
2280
	 * @param bool $remember
2281
	 * @return static
2282
	 */
2283
	public function setRememberState($remember = TRUE)
2284
	{
2285
		$this->remember_state = (bool) $remember;
2286
2287
		return $this;
2288
	}
2289
2290
2291
	/**
2292
	 * Should datagrid refresh url using history API?
2293
	 * @param bool $refresh
2294
	 * @return static
2295
	 */
2296
	public function setRefreshUrl($refresh = TRUE)
2297
	{
2298
		$this->refresh_url = (bool) $refresh;
2299
2300
2301
		return $this;
2302
	}
2303
2304
2305
	/**
2306
	 * Get session data if functionality is enabled
2307
	 * @param  string $key
2308
	 * @return mixed
2309
	 */
2310
	public function getSessionData($key = NULL, $default_value = NULL)
2311
	{
2312
		if (!$this->remember_state) {
2313
			return $key ? $default_value : [];
2314
		}
2315
2316
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2317
	}
2318
2319
2320
	/**
2321
	 * Save session data - just if it is enabled
2322
	 * @param  string $key
2323
	 * @param  mixed  $value
2324
	 * @return void
2325
	 */
2326
	public function saveSessionData($key, $value)
2327
	{
2328
		if ($this->remember_state) {
2329
			$this->grid_session->{$key} = $value;
2330
		}
2331
	}
2332
2333
2334
	/**
2335
	 * Delete session data
2336
	 * @return void
2337
	 */
2338
	public function deleteSesssionData($key)
2339
	{
2340
		unset($this->grid_session->{$key});
2341
	}
2342
2343
2344
	/********************************************************************************
2345
	 *                                  ITEM DETAIL                                 *
2346
	 ********************************************************************************/
2347
2348
2349
	/**
2350
	 * Get items detail parameters
2351
	 * @return array
2352
	 */
2353
	public function getItemsDetail()
2354
	{
2355
		return $this->items_detail;
2356
	}
2357
2358
2359
	/**
2360
	 * Items can have thair detail - toggled
2361
	 * @param mixed $detail callable|string|bool
2362
	 * @param bool|NULL $primary_where_column
2363
	 * @return Column\ItemDetail
2364
	 */
2365
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2366
	{
2367
		if ($this->isSortable()) {
2368
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2369
		}
2370
2371
		$this->items_detail = new Column\ItemDetail(
2372
			$this,
2373
			$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...
2374
		);
2375
2376
		if (is_string($detail)) {
2377
			/**
2378
			 * Item detail will be in separate template
2379
			 */
2380
			$this->items_detail->setType('template');
2381
			$this->items_detail->setTemplate($detail);
2382
2383
		} else if (is_callable($detail)) {
2384
			/**
2385
			 * Item detail will be rendered via custom callback renderer
2386
			 */
2387
			$this->items_detail->setType('renderer');
2388
			$this->items_detail->setRenderer($detail);
2389
2390
		} else if (TRUE === $detail) {
2391
			/**
2392
			 * Item detail will be rendered probably via block #detail
2393
			 */
2394
			$this->items_detail->setType('block');
2395
2396
		} else {
2397
			throw new DataGridException(
2398
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2399
			);
2400
		}
2401
2402
		return $this->items_detail;
2403
	}
2404
2405
2406
	/**
2407
	 * @param callable $callable_set_container 
2408
	 * @return static
2409
	 */
2410
	public function setItemsDetailForm(callable $callable_set_container)
2411
	{
2412
		if ($this->items_detail instanceof Column\ItemDetail) {
2413
			$this->items_detail->setForm(
2414
				new Utils\ItemDetailForm($callable_set_container)
2415
			);
2416
2417
			return $this;
2418
		}
2419
2420
		throw new DataGridException('Please set the ItemDetail first.');
2421
	}
2422
2423
2424
	/**
2425
	 * @return Nette\Forms\Container|NULL
2426
	 */
2427
	public function getItemDetailForm()
2428
	{
2429
		if ($this->items_detail instanceof Column\ItemDetail) {
2430
			return $this->items_detail->getForm();
2431
		}
2432
2433
		return NULL;
2434
	}
2435
2436
2437
	/********************************************************************************
2438
	 *                                ROW PRIVILEGES                                *
2439
	 ********************************************************************************/
2440
2441
2442
	/**
2443
	 * @param  callable $condition
2444
	 * @return void
2445
	 */
2446
	public function allowRowsGroupAction(callable $condition)
2447
	{
2448
		$this->row_conditions['group_action'] = $condition;
2449
	}
2450
2451
2452
	/**
2453
	 * @param  string   $key
2454
	 * @param  callable $condition
2455
	 * @return void
2456
	 */
2457
	public function allowRowsAction($key, callable $condition)
2458
	{
2459
		$this->row_conditions['action'][$key] = $condition;
2460
	}
2461
2462
2463
	/**
2464
	 * @param  string      $name
2465
	 * @param  string|null $key
2466
	 * @return bool|callable
2467
	 */
2468
	public function getRowCondition($name, $key = NULL)
2469
	{
2470
		if (!isset($this->row_conditions[$name])) {
2471
			return FALSE;
2472
		}
2473
2474
		$condition = $this->row_conditions[$name];
2475
2476
		if (!$key) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $key of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2477
			return $condition;
2478
		}
2479
2480
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2481
	}
2482
2483
2484
	/********************************************************************************
2485
	 *                               COLUMN CALLBACK                                *
2486
	 ********************************************************************************/
2487
2488
2489
	/**
2490
	 * @param  string   $key
2491
	 * @param  callable $callback
2492
	 * @return void
2493
	 */
2494
	public function addColumnCallback($key, callable $callback)
2495
	{
2496
		$this->column_callbacks[$key] = $callback;
2497
	}
2498
2499
2500
	/**
2501
	 * @param  string $key
2502
	 * @return callable|null
2503
	 */
2504
	public function getColumnCallback($key)
2505
	{
2506
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
2507
	}
2508
2509
2510
	/********************************************************************************
2511
	 *                                 INLINE EDIT                                  *
2512
	 ********************************************************************************/
2513
2514
2515
	/**
2516
	 * @return InlineEdit
2517
	 */
2518
	public function addInlineEdit($primary_where_column = NULL)
2519
	{
2520
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2521
2522
		return $this->inlineEdit;
2523
	}
2524
2525
2526
	/**
2527
	 * @return InlineEdit|null
2528
	 */
2529
	public function getInlineEdit()
2530
	{
2531
		return $this->inlineEdit;
2532
	}
2533
2534
2535
	/**
2536
	 * @param  mixed $id
2537
	 * @return void
2538
	 */
2539
	public function handleInlineEdit($id)
2540
	{
2541
		if ($this->inlineEdit) {
2542
			$this->inlineEdit->setItemId($id);
2543
2544
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2545
2546
			$this['filter']['inline_edit']->addHidden('_id', $id);
2547
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2548
2549
			$this->redrawItem($id, $primary_where_column);
2550
		}
2551
	}
2552
2553
2554
	/********************************************************************************
2555
	 *                                  INLINE ADD                                  *
2556
	 ********************************************************************************/
2557
2558
2559
	/**
2560
	 * @return InlineEdit
2561
	 */
2562
	public function addInlineAdd()
2563
	{
2564
		$this->inlineAdd = new InlineEdit($this);
2565
2566
		$this->inlineAdd
2567
			->setIcon('plus')
2568
			->setClass('btn btn-xs btn-default');
2569
2570
		return $this->inlineAdd;
2571
	}
2572
2573
2574
	/**
2575
	 * @return InlineEdit|null
2576
	 */
2577
	public function getInlineAdd()
2578
	{
2579
		return $this->inlineAdd;
2580
	}
2581
2582
2583
	/********************************************************************************
2584
	 *                               HIDEABLE COLUMNS                               *
2585
	 ********************************************************************************/
2586
2587
2588
	/**
2589
	 * Can datagrid hide colums?
2590
	 * @return boolean
2591
	 */
2592
	public function canHideColumns()
2593
	{
2594
		return (bool) $this->can_hide_columns;
2595
	}
2596
2597
2598
	/**
2599
	 * Order Grid to set columns hideable.
2600
	 * @return static
2601
	 */
2602
	public function setColumnsHideable()
2603
	{
2604
		$this->can_hide_columns = TRUE;
2605
2606
		return $this;
2607
	}
2608
2609
2610
	/********************************************************************************
2611
	 *                                   INTERNAL                                   *
2612
	 ********************************************************************************/
2613
2614
2615
	/**
2616
	 * Get cont of columns
2617
	 * @return int
2618
	 */
2619
	public function getColumnsCount()
2620
	{
2621
		$count = sizeof($this->getColumns());
2622
2623
		if (!empty($this->actions)
2624
			|| $this->isSortable()
2625
			|| $this->getItemsDetail()
2626
			|| $this->getInlineEdit()
2627
			|| $this->getInlineAdd()) {
2628
			$count++;
2629
		}
2630
2631
		if ($this->hasGroupActions()) {
2632
			$count++;
2633
		}
2634
2635
		return $count;
2636
	}
2637
2638
2639
	/**
2640
	 * Get primary key of datagrid data source
2641
	 * @return string
2642
	 */
2643
	public function getPrimaryKey()
2644
	{
2645
		return $this->primary_key;
2646
	}
2647
2648
2649
	/**
2650
	 * Get set of set columns
2651
	 * @return Column\IColumn[]
2652
	 */
2653
	public function getColumns()
2654
	{
2655
		if (!$this->getSessionData('_grid_hidden_columns_manipulated', FALSE)) {
2656
			$columns_to_hide = [];
2657
2658
			foreach ($this->columns as $key => $column) {
2659
				if ($column->getDefaultHide()) {
2660
					$columns_to_hide[] = $key;
2661
				}
2662
			}
2663
2664
			if (!empty($columns_to_hide)) {
2665
				$this->saveSessionData('_grid_hidden_columns', $columns_to_hide);
2666
				$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2667
			}
2668
		}
2669
2670
		$hidden_columns = $this->getSessionData('_grid_hidden_columns', []);
2671
		
2672
		foreach ($hidden_columns as $column) {
2673
			if (!empty($this->columns[$column])) {
2674
				$this->columns_visibility[$column] = [
2675
					'visible' => FALSE,
2676
					'name' => $this->columns[$column]->getName()
2677
				];
2678
2679
				$this->removeColumn($column);
2680
			}
2681
		}
2682
2683
		return $this->columns;
2684
	}
2685
2686
2687
	/**
2688
	 * @return PresenterComponent
2689
	 */
2690
	public function getParent()
2691
	{
2692
		$parent = parent::getParent();
2693
2694
		if (!($parent instanceof PresenterComponent)) {
2695
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2696
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2697
			);
2698
		}
2699
2700
		return $parent;
2701
	}
2702
2703
2704
	/**
2705
	 * Some of datagrid columns is hidden by default
2706
	 * @param bool $default_hide
2707
	 */
2708
	public function setSomeColumnDefaultHide($default_hide)
2709
	{
2710
		$this->some_column_default_hide = $default_hide;
2711
	}
2712
2713
2714
	/**
2715
	 * Are some of columns hidden bydefault?
2716
	 */
2717
	public function hasSomeColumnDefaultHide()
2718
	{
2719
		return $this->some_column_default_hide;
2720
	}
2721
2722
}
2723