Completed
Push — master ( b0256d...66d43c )
by Pavel
02:53
created

DataGrid::hasTreeViewChildrenCallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
754
755
		$this->columns_visibility[$key] = [
756
			'visible' => TRUE,
757
			'name' => $column->getName()
758
		];
759
760
		return $this->columns[$key] = $column;
761
	}
762
763
764
	/**
765
	 * Return existing column
766
	 * @param  string $key
767
	 * @return Column\Column
768
	 * @throws DataGridException
769
	 */
770
	public function getColumn($key)
771
	{
772
		if (!isset($this->columns[$key])) {
773
			throw new DataGridException("There is no column at key [$key] defined.");
774
		}
775
776
		return $this->columns[$key];
777
	}
778
779
780
	/**
781
	 * Remove column
782
	 * @param string $key
783
	 * @return void
784
	 */
785
	public function removeColumn($key)
786
	{
787
		unset($this->columns[$key]);
788
	}
789
790
791
	/**
792
	 * Check whether given key already exists in $this->columns
793
	 * @param  string $key
794
	 * @throws DataGridException
795
	 */
796
	protected function addColumnCheck($key)
797
	{
798
		if (isset($this->columns[$key])) {
799
			throw new DataGridException("There is already column at key [$key] defined.");
800
		}
801
	}
802
803
804
	/********************************************************************************
805
	 *                                    ACTIONS                                   *
806
	 ********************************************************************************/
807
808
809
	/**
810
	 * Create action
811
	 * @param string     $key
812
	 * @param string     $name
813
	 * @param string     $href
814
	 * @param array|null $params
815
	 * @return Column\Action
816
	 */
817
	public function addAction($key, $name, $href = NULL, array $params = NULL)
818
	{
819
		$this->addActionCheck($key);
820
		$href = $href ?: $key;
821
822
		if (NULL === $params) {
823
			$params = [$this->primary_key];
824
		}
825
826
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
827
	}
828
829
830
	/**
831
	 * Create action callback
832
	 * @param string     $key
833
	 * @param string     $name
834
	 * @return Column\Action
835
	 */
836
	public function addActionCallback($key, $name, $callback = NULL)
837
	{
838
		$this->addActionCheck($key);
839
		$params = ['__id' => $this->primary_key];
840
841
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
842
843
		if ($callback) {
844
			if (!is_callable($callback)) {
845
				throw new DataGridException('ActionCallback callback has to be callable.');
846
			}
847
848
			$action->onClick[] = $callback;
849
		}
850
851
		return $action;
852
	}
853
854
855
	/**
856
	 * Get existing action
857
	 * @param  string       $key
858
	 * @return Column\Action
859
	 * @throws DataGridException
860
	 */
861
	public function getAction($key)
862
	{
863
		if (!isset($this->actions[$key])) {
864
			throw new DataGridException("There is no action at key [$key] defined.");
865
		}
866
867
		return $this->actions[$key];
868
	}
869
870
871
	/**
872
	 * Remove action
873
	 * @param string $key
874
	 * @return void
875
	 */
876
	public function removeAction($key)
877
	{
878
		unset($this->actions[$key]);
879
	}
880
881
882
	/**
883
	 * Check whether given key already exists in $this->filters
884
	 * @param  string $key
885
	 * @throws DataGridException
886
	 */
887
	protected function addActionCheck($key)
888
	{
889
		if (isset($this->actions[$key])) {
890
			throw new DataGridException("There is already action at key [$key] defined.");
891
		}
892
	}
893
894
895
	/********************************************************************************
896
	 *                                    FILTERS                                   *
897
	 ********************************************************************************/
898
899
900
	/**
901
	 * Add filter fot text search
902
	 * @param string       $key
903
	 * @param string       $name
904
	 * @param array|string $columns
905
	 * @return Filter\FilterText
906
	 * @throws DataGridException
907
	 */
908
	public function addFilterText($key, $name, $columns = NULL)
909
	{
910
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
911
912
		if (!is_array($columns)) {
913
			throw new DataGridException("Filter Text can except only array or string.");
914
		}
915
916
		$this->addFilterCheck($key);
917
918
		return $this->filters[$key] = new Filter\FilterText($key, $name, $columns);
919
	}
920
921
922
	/**
923
	 * Add select box filter
924
	 * @param string $key
925
	 * @param string $name
926
	 * @param array  $options
927
	 * @param string $column
928
	 * @return Filter\FilterSelect
929
	 * @throws DataGridException
930
	 */
931 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...
932
	{
933
		$column = $column ?: $key;
934
935
		if (!is_string($column)) {
936
			throw new DataGridException("Filter Select can only filter through one column.");
937
		}
938
939
		$this->addFilterCheck($key);
940
941
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
942
	}
943
944
945
	/**
946
	 * Add datepicker filter
947
	 * @param string $key
948
	 * @param string $name
949
	 * @param string $column
950
	 * @return Filter\FilterDate
951
	 * @throws DataGridException
952
	 */
953
	public function addFilterDate($key, $name, $column = NULL)
954
	{
955
		$column = $column ?: $key;
956
957
		if (!is_string($column)) {
958
			throw new DataGridException("FilterDate can only filter through one column.");
959
		}
960
961
		$this->addFilterCheck($key);
962
963
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
964
	}
965
966
967
	/**
968
	 * Add range filter (from - to)
969
	 * @param string $key
970
	 * @param string $name
971
	 * @param string $column
972
	 * @return Filter\FilterRange
973
	 * @throws DataGridException
974
	 */
975 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...
976
	{
977
		$column = $column ?: $key;
978
979
		if (!is_string($column)) {
980
			throw new DataGridException("FilterRange can only filter through one column.");
981
		}
982
983
		$this->addFilterCheck($key);
984
985
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
986
	}
987
988
989
	/**
990
	 * Add datepicker filter (from - to)
991
	 * @param string $key
992
	 * @param string $name
993
	 * @param string $column
994
	 * @return Filter\FilterDateRange
995
	 * @throws DataGridException
996
	 */
997 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...
998
	{
999
		$column = $column ?: $key;
1000
1001
		if (!is_string($column)) {
1002
			throw new DataGridException("FilterDateRange can only filter through one column.");
1003
		}
1004
1005
		$this->addFilterCheck($key);
1006
1007
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
1008
	}
1009
1010
1011
	/**
1012
	 * Check whether given key already exists in $this->filters
1013
	 * @param  string $key
1014
	 * @throws DataGridException
1015
	 */
1016
	protected function addFilterCheck($key)
1017
	{
1018
		if (isset($this->filters[$key])) {
1019
			throw new DataGridException("There is already action at key [$key] defined.");
1020
		}
1021
	}
1022
1023
1024
	/**
1025
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1026
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1027
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1028
	 */
1029
	public function assableFilters()
1030
	{
1031
		foreach ($this->filter as $key => $value) {
1032
			if (!isset($this->filters[$key])) {
1033
				$this->deleteSesssionData($key);
1034
1035
				continue;
1036
			}
1037
1038
			if (is_array($value) || $value instanceof \Traversable) {
1039
				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...
1040
					$this->filters[$key]->setValue($value);
1041
				}
1042
			} else {
1043
				if ($value !== '' && $value !== NULL) {
1044
					$this->filters[$key]->setValue($value);
1045
				}
1046
			}
1047
		}
1048
1049
		foreach ($this->columns as $column) {
1050
			if (isset($this->sort[$column->getSortingColumn()])) {
1051
				$column->setSort($this->sort);
1052
			}
1053
		}
1054
1055
		return $this->filters;
1056
	}
1057
1058
1059
	/**
1060
	 * Remove filter
1061
	 * @param string $key
1062
	 * @return void
1063
	 */
1064
	public function removeFilter($key)
1065
	{
1066
		unset($this->filters[$key]);
1067
	}
1068
1069
1070
	/********************************************************************************
1071
	 *                                  FILTERING                                   *
1072
	 ********************************************************************************/
1073
1074
1075
	/**
1076
	 * Is filter active?
1077
	 * @return boolean
1078
	 */
1079
	public function isFilterActive()
1080
	{
1081
		$is_filter = ArraysHelper::testTruthy($this->filter);
1082
1083
		return ($is_filter) || $this->force_filter_active;
1084
	}
1085
1086
1087
	/**
1088
	 * Tell that filter is active from whatever reasons
1089
	 * return static
1090
	 */
1091
	public function setFilterActive()
1092
	{
1093
		$this->force_filter_active = TRUE;
1094
1095
		return $this;
1096
	}
1097
1098
1099
	/**
1100
	 * If we want to sent some initial filter
1101
	 * @param array $filter
1102
	 * @return static
1103
	 */
1104
	public function setFilter(array $filter)
1105
	{
1106
		$this->filter = $filter;
1107
1108
		return $this;
1109
	}
1110
1111
1112
	/**
1113
	 * FilterAndGroupAction form factory
1114
	 * @return Form
1115
	 */
1116
	public function createComponentFilter()
1117
	{
1118
		$form = new Form($this, 'filter');
1119
1120
		$form->setMethod('get');
1121
1122
		$form->setTranslator($this->getTranslator());
1123
1124
		/**
1125
		 * InlineEdit part
1126
		 */
1127
		$inline_edit_container = $form->addContainer('inline_edit');
1128
1129
		if ($this->inlineEdit instanceof InlineEdit) {
1130
			$inline_edit_container->addSubmit('submit', 'Save');
1131
			$inline_edit_container->addSubmit('cancel', 'Cancel');
1132
1133
			$this->inlineEdit->onControlAdd($inline_edit_container);
0 ignored issues
show
Documentation Bug introduced by
The method onControlAdd does not exist on object<Ublaboo\DataGrid\InlineEdit\InlineEdit>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1134
		}
1135
1136
		/**
1137
		 * Filter part
1138
		 */
1139
		$filter_container = $form->addContainer('filter');
1140
1141
		foreach ($this->filters as $filter) {
1142
			$filter->addToFormContainer($filter_container);
1143
		}
1144
1145
		/**
1146
		 * Group action part
1147
		 */
1148
		$group_action_container = $form->addContainer('group_action');
1149
1150
		if ($this->hasGroupActions()) {
1151
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1152
		}
1153
1154
		$form->setDefaults(['filter' => $this->filter]);
1155
1156
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1157
1158
		return $form;
1159
	}
1160
1161
1162
	/**
1163
	 * Set $this->filter values after filter form submitted
1164
	 * @param  Form $form
1165
	 * @return void
1166
	 */
1167
	public function filterSucceeded(Form $form)
1168
	{
1169
		$values = $form->getValues();
1170
1171
		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...
1172
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1173
				return;
1174
			}
1175
		}
1176
1177
		if (isset($form['inline_edit'])) {
1178
			$inline_edit = $form['inline_edit'];
1179
1180
			if ($inline_edit['submit']->isSubmittedBy() || $inline_edit['cancel']->isSubmittedBy()) {
1181
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1182
				$primary_where_column = $form->getHttpData(
1183
					Form::DATA_LINE,
1184
					'inline_edit[_primary_where_column]'
1185
				);
1186
1187
				if ($inline_edit['submit']->isSubmittedBy()) {
1188
					$this->inlineEdit->onSubmit($values->inline_edit);
0 ignored issues
show
Documentation Bug introduced by
The method onSubmit does not exist on object<Ublaboo\DataGrid\InlineEdit\InlineEdit>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1189
1190
					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...
1191
						$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...
1192
					}
1193
				}
1194
1195
				$this->redrawItem($id, $primary_where_column);
1196
1197
				return;
1198
			}
1199
		}
1200
1201
		$values = $values['filter'];
1202
1203
		foreach ($values as $key => $value) {
1204
			/**
1205
			 * Session stuff
1206
			 */
1207
			$this->saveSessionData($key, $value);
1208
1209
			/**
1210
			 * Other stuff
1211
			 */
1212
			$this->filter[$key] = $value;
1213
		}
1214
1215
		$this->reload();
1216
	}
1217
1218
1219
	/**
1220
	 * Should be datagrid filters rendered separately?
1221
	 * @param boolean $out
1222
	 * @return static
1223
	 */
1224
	public function setOuterFilterRendering($out = TRUE)
1225
	{
1226
		$this->outer_filter_rendering = (bool) $out;
1227
1228
		return $this;
1229
	}
1230
1231
1232
	/**
1233
	 * Are datagrid filters rendered separately?
1234
	 * @return boolean
1235
	 */
1236
	public function hasOuterFilterRendering()
1237
	{
1238
		return $this->outer_filter_rendering;
1239
	}
1240
1241
1242
	/**
1243
	 * Try to restore session stuff
1244
	 * @return void
1245
	 */
1246
	public function findSessionFilters()
1247
	{
1248
		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...
1249
			return;
1250
		}
1251
1252
		if (!$this->remember_state) {
1253
			return;
1254
		}
1255
1256
		if ($page = $this->getSessionData('_grid_page')) {
1257
			$this->page = $page;
1258
		}
1259
1260
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1261
			$this->per_page = $per_page;
1262
		}
1263
1264
		if ($sort = $this->getSessionData('_grid_sort')) {
1265
			$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...
1266
		}
1267
1268
		foreach ($this->getSessionData() as $key => $value) {
1269
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1270
				$this->filter[$key] = $value;
1271
			}
1272
		}
1273
	}
1274
1275
1276
	/********************************************************************************
1277
	 *                                    EXPORTS                                   *
1278
	 ********************************************************************************/
1279
1280
1281
	/**
1282
	 * Add export of type callback
1283
	 * @param string $text
1284
	 * @param callable $callback
1285
	 * @param boolean $filtered
1286
	 * @return Export\Export
1287
	 */
1288
	public function addExportCallback($text, $callback, $filtered = FALSE)
1289
	{
1290
		if (!is_callable($callback)) {
1291
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1292
		}
1293
1294
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1295
	}
1296
1297
1298
	/**
1299
	 * Add already implemented csv export
1300
	 * @param string $text
1301
	 * @param string $csv_file_name
1302
	 * @return Export\Export
1303
	 */
1304
	public function addExportCsv($text, $csv_file_name)
1305
	{
1306
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1307
	}
1308
1309
1310
	/**
1311
	 * Add already implemented csv export, but for filtered data
1312
	 * @param string $text
1313
	 * @param string $csv_file_name
1314
	 * @return Export\Export
1315
	 */
1316
	public function addExportCsvFiltered($text, $csv_file_name)
1317
	{
1318
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1319
	}
1320
1321
1322
	/**
1323
	 * Add export to array
1324
	 * @param Export\Export $export
1325
	 * @return Export\Export
1326
	 */
1327
	protected function addToExports(Export\Export $export)
1328
	{
1329
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1330
1331
		$export->setLink($this->link('export!', ['id' => $id]));
1332
1333
		return $this->exports[$id] = $export;
1334
	}
1335
1336
1337
	public function resetExportsLinks()
1338
	{
1339
		foreach ($this->exports as $id => $export) {
1340
			$export->setLink($this->link('export!', ['id' => $id]));
1341
		}
1342
	}
1343
1344
1345
	/********************************************************************************
1346
	 *                                 GROUP ACTIONS                                *
1347
	 ********************************************************************************/
1348
1349
1350
	/**
1351
	 * Add group actino
1352
	 * @param string $title
1353
	 * @param array  $options
1354
	 * @return GroupAction\GroupAction
1355
	 */
1356
	public function addGroupAction($title, $options = [])
1357
	{
1358
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1359
	}
1360
1361
1362
	/**
1363
	 * Get collection of all group actions
1364
	 * @return GroupAction\GroupActionCollection
1365
	 */
1366
	public function getGroupActionCollection()
1367
	{
1368
		if (!$this->group_action_collection) {
1369
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1370
		}
1371
1372
		return $this->group_action_collection;
1373
	}
1374
1375
1376
	/**
1377
	 * Has datagrid some group actions?
1378
	 * @return boolean
1379
	 */
1380
	public function hasGroupActions()
1381
	{
1382
		return (bool) $this->group_action_collection;
1383
	}
1384
1385
1386
	/********************************************************************************
1387
	 *                                   HANDLERS                                   *
1388
	 ********************************************************************************/
1389
1390
1391
	/**
1392
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1393
	 * @param  int  $page
1394
	 * @return void
1395
	 */
1396
	public function handlePage($page)
1397
	{
1398
		/**
1399
		 * Session stuff
1400
		 */
1401
		$this->page = $page;
1402
		$this->saveSessionData('_grid_page', $page);
1403
1404
		$this->reload(['table']);
1405
	}
1406
1407
1408
	/**
1409
	 * Handler for sorting
1410
	 * @param array $sort
1411
	 * @return void
1412
	 */
1413
	public function handleSort(array $sort)
1414
	{
1415
		$new_sort = [];
1416
1417
		/**
1418
		 * Find apropirate column
1419
		 */
1420
		foreach ($sort as $key => $value) {
1421
			if (empty($this->columns[$key])) {
1422
				throw new DataGridException("Column <$key> not found");
1423
			}
1424
1425
			$column = $this->columns[$key];
1426
			$new_sort = [$column->getSortingColumn() => $value];
1427
1428
			/**
1429
			 * Pagination may be reseted after sorting
1430
			 */
1431
			if ($column->sortableResetPagination()) {
1432
				$this->page = 1;
1433
				$this->saveSessionData('_grid_page', 1);
1434
			}
1435
1436
			/**
1437
			 * Custom sorting callback may be applied
1438
			 */
1439
			if ($column->getSortableCallback()) {
1440
				$this->sort_callback = $column->getSortableCallback();
1441
			}
1442
		}
1443
1444
		/**
1445
		 * Session stuff
1446
		 */
1447
		$this->sort = $new_sort;
1448
		$this->saveSessionData('_grid_sort', $this->sort);
1449
1450
		$this->reload(['table']);
1451
	}
1452
1453
1454
	/**
1455
	 * handler for reseting the filter
1456
	 * @return void
1457
	 */
1458
	public function handleResetFilter()
1459
	{
1460
		/**
1461
		 * Session stuff
1462
		 */
1463
		$this->deleteSesssionData('_grid_page');
1464
1465
		foreach ($this->getSessionData() as $key => $value) {
1466
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1467
				$this->deleteSesssionData($key);
1468
			}
1469
		}
1470
1471
		$this->filter = [];
1472
1473
		$this->reload(['grid']);
1474
	}
1475
1476
1477
	/**
1478
	 * Handler for export
1479
	 * @param  int $id Key for particular export class in array $this->exports
1480
	 * @return void
1481
	 */
1482
	public function handleExport($id)
1483
	{
1484
		if (!isset($this->exports[$id])) {
1485
			throw new Nette\Application\ForbiddenRequestException;
1486
		}
1487
1488
		if (!empty($this->columns_export_order)) {
1489
			$this->setColumnsOrder($this->columns_export_order);
1490
		}
1491
1492
		$export = $this->exports[$id];
1493
1494
		if ($export->isFiltered()) {
1495
			$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...
1496
			$filter    = $this->assableFilters();
1497
		} else {
1498
			$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...
1499
			$filter    = [];
1500
		}
1501
1502
		if (NULL === $this->dataModel) {
1503
			throw new DataGridException('You have to set a data source first.');
1504
		}
1505
1506
		$rows = [];
1507
1508
		$items = Nette\Utils\Callback::invokeArgs(
1509
			[$this->dataModel, 'filterData'], [
1510
				NULL,
1511
				new Sorting($this->sort, $this->sort_callback),
1512
				$filter
1513
			]
1514
		);
1515
1516
		foreach ($items as $item) {
1517
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1518
		}
1519
1520
		if ($export instanceof Export\ExportCsv) {
1521
			$export->invoke($rows, $this);
1522
		} else {
1523
			$export->invoke($items, $this);
1524
		}
1525
1526
		if ($export->isAjax()) {
1527
			$this->reload();
1528
		}
1529
	}
1530
1531
1532
	/**
1533
	 * Handler for getting children of parent item (e.g. category)
1534
	 * @param  int $parent
1535
	 * @return void
1536
	 */
1537
	public function handleGetChildren($parent)
1538
	{
1539
		$this->setDataSource(
1540
			call_user_func($this->tree_view_children_callback, $parent)
1541
		);
1542
1543
		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...
1544
			$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...
1545
			$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...
1546
1547
			$this->redrawControl('items');
1548
1549
			$this->onRedraw();
1550
		} else {
1551
			$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...
1552
		}
1553
	}
1554
1555
1556
	/**
1557
	 * Handler for getting item detail
1558
	 * @param  mixed $id
1559
	 * @return void
1560
	 */
1561
	public function handleGetItemDetail($id)
1562
	{
1563
		$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...
1564
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1565
1566
		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...
1567
			$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...
1568
			$this->redrawControl('items');
1569
1570
			$this->onRedraw();
1571
		} else {
1572
			$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...
1573
		}
1574
	}
1575
1576
1577
	/**
1578
	 * Handler for inline editing
1579
	 * @param  mixed $id
1580
	 * @param  mixed $key
1581
	 * @return void
1582
	 */
1583
	public function handleEdit($id, $key)
1584
	{
1585
		$column = $this->getColumn($key);
1586
		$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...
1587
1588
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1589
	}
1590
1591
1592
	/**
1593
	 * Redraw $this
1594
	 * @return void
1595
	 */
1596
	public function reload($snippets = [])
1597
	{
1598
		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...
1599
			$this->redrawControl('tbody');
1600
			$this->redrawControl('pagination');
1601
1602
			/**
1603
			 * manualy reset exports links...
1604
			 */
1605
			$this->resetExportsLinks();
1606
			$this->redrawControl('exports');
1607
1608
			foreach ($snippets as $snippet) {
1609
				$this->redrawControl($snippet);
1610
			}
1611
1612
			$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...
1613
1614
			$this->onRedraw();
1615
		} else {
1616
			$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...
1617
		}
1618
	}
1619
1620
1621
	/**
1622
	 * Handler for column status
1623
	 * @param  string $id
1624
	 * @param  string $key
1625
	 * @param  string $value
1626
	 * @return void
1627
	 */
1628
	public function handleChangeStatus($id, $key, $value)
1629
	{
1630
		if (empty($this->columns[$key])) {
1631
			throw new DataGridException("ColumnStatus[$key] does not exist");
1632
		}
1633
1634
		$this->columns[$key]->onChange($id, $value);
1635
	}
1636
1637
1638
	/**
1639
	 * Redraw just one row via ajax
1640
	 * @param  int   $id
1641
	 * @param  mixed $primary_where_column
1642
	 * @return void
1643
	 */
1644
	public function redrawItem($id, $primary_where_column = NULL)
1645
	{
1646
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1647
1648
		$this->redrawControl('items');
1649
		$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...
1650
1651
		$this->onRedraw();
1652
	}
1653
1654
1655
	/**
1656
	 * Tell datagrid to display all columns
1657
	 * @return void
1658
	 */
1659
	public function handleShowAllColumns()
1660
	{
1661
		$this->deleteSesssionData('_grid_hidden_columns');
1662
1663
		$this->redrawControl();
1664
1665
		$this->onRedraw();
1666
	}
1667
1668
1669
	/**
1670
	 * Reveal particular column
1671
	 * @param  string $column
1672
	 * @return void
1673
	 */
1674 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...
1675
	{
1676
		$columns = $this->getSessionData('_grid_hidden_columns');
1677
1678
		if (!empty($columns)) {
1679
			$pos = array_search($column, $columns);
1680
1681
			if ($pos !== FALSE) {
1682
				unset($columns[$pos]);
1683
			}
1684
		}
1685
1686
		$this->saveSessionData('_grid_hidden_columns', $columns);
1687
1688
		$this->redrawControl();
1689
1690
		$this->onRedraw();
1691
	}
1692
1693
1694
	/**
1695
	 * Notice datagrid to not display particular columns
1696
	 * @param  string $column
1697
	 * @return void
1698
	 */
1699 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...
1700
	{
1701
		/**
1702
		 * Store info about hiding a column to session
1703
		 */
1704
		$columns = $this->getSessionData('_grid_hidden_columns');
1705
1706
		if (empty($columns)) {
1707
			$columns = [$column];
1708
		} else if (!in_array($column, $columns)) {
1709
			array_push($columns, $column);
1710
		}
1711
1712
		$this->saveSessionData('_grid_hidden_columns', $columns);
1713
1714
		$this->redrawControl();
1715
1716
		$this->onRedraw();
1717
	}
1718
1719
1720
	public function handleActionCallback($__key, $__id)
1721
	{
1722
		$action = $this->getAction($__key);
1723
1724
		if (!($action instanceof Column\ActionCallback)) {
1725
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1726
		}
1727
1728
		$action->onClick($__id);
1729
	}
1730
1731
1732
	/********************************************************************************
1733
	 *                                  PAGINATION                                  *
1734
	 ********************************************************************************/
1735
1736
1737
	/**
1738
	 * Set options of select "items_per_page"
1739
	 * @param array $items_per_page_list
1740
	 * @return static
1741
	 */
1742
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1743
	{
1744
		$this->items_per_page_list = $items_per_page_list;
1745
1746
		if ($include_all) {
1747
			$this->items_per_page_list[] = 'all';
1748
		}
1749
1750
		return $this;
1751
	}
1752
1753
1754
	/**
1755
	 * Paginator factory
1756
	 * @return Components\DataGridPaginator\DataGridPaginator
1757
	 */
1758
	public function createComponentPaginator()
1759
	{
1760
		/**
1761
		 * Init paginator
1762
		 */
1763
		$component = new Components\DataGridPaginator\DataGridPaginator(
1764
			$this->getTranslator()
1765
		);
1766
		$paginator = $component->getPaginator();
1767
1768
		$paginator->setPage($this->page);
1769
		$paginator->setItemsPerPage($this->getPerPage());
1770
1771
		return $component;
1772
	}
1773
1774
1775
	/**
1776
	 * PerPage form factory
1777
	 * @return Form
1778
	 */
1779
	public function createComponentPerPage()
1780
	{
1781
		$form = new Form;
1782
1783
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1784
			->setValue($this->getPerPage());
1785
1786
		$form->addSubmit('submit', '');
1787
1788
		$saveSessionData = [$this, 'saveSessionData'];
1789
1790
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1791
			/**
1792
			 * Session stuff
1793
			 */
1794
			$saveSessionData('_grid_per_page', $values->per_page);
1795
1796
			/**
1797
			 * Other stuff
1798
			 */
1799
			$this->per_page = $values->per_page;
1800
			$this->reload();
1801
		};
1802
1803
		return $form;
1804
	}
1805
1806
1807
	/**
1808
	 * Get parameter per_page
1809
	 * @return int
1810
	 */
1811
	public function getPerPage()
1812
	{
1813
		$items_per_page_list = $this->getItemsPerPageList();
1814
1815
		$per_page = $this->per_page ?: reset($items_per_page_list);
1816
1817
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
1818
			$per_page = reset($items_per_page_list);
1819
		}
1820
1821
		return $per_page;
1822
	}
1823
1824
1825
	/**
1826
	 * Get associative array of items_per_page_list
1827
	 * @return array
1828
	 */
1829
	public function getItemsPerPageList()
1830
	{
1831
		if (empty($this->items_per_page_list)) {
1832
			$this->setItemsPerPageList([10, 20, 50], TRUE);
1833
		}
1834
1835
		$list = array_flip($this->items_per_page_list);
1836
1837
		foreach ($list as $key => $value) {
1838
			$list[$key] = $key;
1839
		}
1840
1841
		if (array_key_exists('all', $list)) {
1842
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
1843
		}
1844
1845
		return $list;
1846
	}
1847
1848
1849
	/**
1850
	 * Order Grid to "be paginated"
1851
	 * @param bool $do
1852
	 * @return static
1853
	 */
1854
	public function setPagination($do)
1855
	{
1856
		$this->do_paginate = (bool) $do;
1857
1858
		return $this;
1859
	}
1860
1861
1862
	/**
1863
	 * Tell whether Grid is paginated
1864
	 * @return bool
1865
	 */
1866
	public function isPaginated()
1867
	{
1868
		return $this->do_paginate;
1869
	}
1870
1871
1872
	/**
1873
	 * Return current paginator class
1874
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1875
	 */
1876
	public function getPaginator()
1877
	{
1878
		if ($this->isPaginated() && $this->per_page !== 'all') {
1879
			return $this['paginator'];
1880
		}
1881
1882
		return NULL;
1883
	}
1884
1885
1886
	/********************************************************************************
1887
	 *                                     I18N                                     *
1888
	 ********************************************************************************/
1889
1890
1891
	/**
1892
	 * Set datagrid translator
1893
	 * @param Nette\Localization\ITranslator $translator
1894
	 * @return static
1895
	 */
1896
	public function setTranslator(Nette\Localization\ITranslator $translator)
1897
	{
1898
		$this->translator = $translator;
1899
1900
		return $this;
1901
	}
1902
1903
1904
	/**
1905
	 * Get translator for datagrid
1906
	 * @return Nette\Localization\ITranslator
1907
	 */
1908
	public function getTranslator()
1909
	{
1910
		if (!$this->translator) {
1911
			$this->translator = new Localization\SimpleTranslator;
1912
		}
1913
1914
		return $this->translator;
1915
	}
1916
1917
1918
	/********************************************************************************
1919
	 *                                 COLUMNS ORDER                                *
1920
	 ********************************************************************************/
1921
1922
1923
	/**
1924
	 * Set order of datagrid columns
1925
	 * @param array $order
1926
	 * @return static
1927
	 */
1928
	public function setColumnsOrder($order)
1929
	{
1930
		$new_order = [];
1931
1932
		foreach ($order as $key) {
1933
			if (isset($this->columns[$key])) {
1934
				$new_order[$key] = $this->columns[$key];
1935
			}
1936
		}
1937
1938
		if (sizeof($new_order) === sizeof($this->columns)) {
1939
			$this->columns = $new_order;
1940
		} else {
1941
			throw new DataGridException('When changing columns order, you have to specify all columns');
1942
		}
1943
1944
		return $this;
1945
	}
1946
1947
1948
	/**
1949
	 * Columns order may be different for export and normal grid
1950
	 * @param array $order
1951
	 */
1952
	public function setColumnsExportOrder($order)
1953
	{
1954
		$this->columns_export_order = (array) $order;
1955
	}
1956
1957
1958
	/********************************************************************************
1959
	 *                                SESSION & URL                                 *
1960
	 ********************************************************************************/
1961
1962
1963
	/**
1964
	 * Find some unique session key name
1965
	 * @return string
1966
	 */
1967
	public function getSessionSectionName()
1968
	{
1969
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
1970
	}
1971
1972
1973
	/**
1974
	 * Should datagrid remember its filters/pagination/etc using session?
1975
	 * @param bool $remember
1976
	 * @return static
1977
	 */
1978
	public function setRememberState($remember = TRUE)
1979
	{
1980
		$this->remember_state = (bool) $remember;
1981
1982
		return $this;
1983
	}
1984
1985
1986
	/**
1987
	 * Should datagrid refresh url using history API?
1988
	 * @param bool $refresh
1989
	 * @return static
1990
	 */
1991
	public function setRefreshUrl($refresh = TRUE)
1992
	{
1993
		$this->refresh_url = (bool) $refresh;
1994
1995
1996
		return $this;
1997
	}
1998
1999
2000
	/**
2001
	 * Get session data if functionality is enabled
2002
	 * @param  string $key
2003
	 * @return mixed
2004
	 */
2005
	public function getSessionData($key = NULL, $default_value = NULL)
2006
	{
2007
		if (!$this->remember_state) {
2008
			return $key ? $default_value : [];
2009
		}
2010
2011
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2012
	}
2013
2014
2015
	/**
2016
	 * Save session data - just if it is enabled
2017
	 * @param  string $key
2018
	 * @param  mixed  $value
2019
	 * @return void
2020
	 */
2021
	public function saveSessionData($key, $value)
2022
	{
2023
		if ($this->remember_state) {
2024
			$this->grid_session->{$key} = $value;
2025
		}
2026
	}
2027
2028
2029
	/**
2030
	 * Delete session data
2031
	 * @return void
2032
	 */
2033
	public function deleteSesssionData($key)
2034
	{
2035
		unset($this->grid_session->{$key});
2036
	}
2037
2038
2039
	/********************************************************************************
2040
	 *                                  ITEM DETAIL                                 *
2041
	 ********************************************************************************/
2042
2043
2044
	/**
2045
	 * Get items detail parameters
2046
	 * @return array
2047
	 */
2048
	public function getItemsDetail()
2049
	{
2050
		return $this->items_detail;
2051
	}
2052
2053
2054
	/**
2055
	 * Items can have thair detail - toggled
2056
	 * @param mixed $detail callable|string|bool
2057
	 * @param bool|NULL $primary_where_column
2058
	 * @return Column\ItemDetail
2059
	 */
2060
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2061
	{
2062
		if ($this->isSortable()) {
2063
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2064
		}
2065
2066
		$this->items_detail = new Column\ItemDetail(
2067
			$this,
2068
			$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...
2069
		);
2070
2071
		if (is_string($detail)) {
2072
			/**
2073
			 * Item detail will be in separate template
2074
			 */
2075
			$this->items_detail->setType('template');
2076
			$this->items_detail->setTemplate($detail);
2077
2078
		} else if (is_callable($detail)) {
2079
			/**
2080
			 * Item detail will be rendered via custom callback renderer
2081
			 */
2082
			$this->items_detail->setType('renderer');
2083
			$this->items_detail->setRenderer($detail);
2084
2085
		} else if (TRUE === $detail) {
2086
			/**
2087
			 * Item detail will be rendered probably via block #detail
2088
			 */
2089
			$this->items_detail->setType('block');
2090
2091
		} else {
2092
			throw new DataGridException(
2093
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2094
			);
2095
		}
2096
2097
		return $this->items_detail;
2098
	}
2099
2100
2101
	/********************************************************************************
2102
	 *                                ROW PRIVILEGES                                *
2103
	 ********************************************************************************/
2104
2105
2106
	public function allowRowsGroupAction(callable $condition)
2107
	{
2108
		$this->row_conditions['group_action'] = $condition;
2109
	}
2110
2111
2112
	public function allowRowsAction($key, callable $condition)
2113
	{
2114
		$this->row_conditions['action'][$key] = $condition;
2115
	}
2116
2117
2118
	public function getRowCondition($name, $key = NULL)
2119
	{
2120
		if (!isset($this->row_conditions[$name])) {
2121
			return FALSE;
2122
		}
2123
2124
		$condition = $this->row_conditions[$name];
2125
2126
		if (!$key) {
2127
			return $condition;
2128
		}
2129
2130
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2131
	}
2132
2133
2134
	/********************************************************************************
2135
	 *                                 INLINE EDIT                                  *
2136
	 ********************************************************************************/
2137
2138
2139
	/**
2140
	 * @return InlineEdit
2141
	 */
2142
	public function addInlineEdit($primary_where_column = NULL)
2143
	{
2144
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2145
2146
		return $this->inlineEdit;
2147
	}
2148
2149
2150
	/**
2151
	 * @return InlineEdit|null
2152
	 */
2153
	public function getInlineEdit()
2154
	{
2155
		return $this->inlineEdit;
2156
	}
2157
2158
2159
	public function handleInlineEdit($id)
2160
	{
2161
		if ($this->inlineEdit) {
2162
			$this->inlineEdit->setItemId($id);
2163
2164
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2165
2166
			$this['filter']['inline_edit']->addHidden('_id', $id);
2167
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2168
2169
			$this->redrawItem($id, $primary_where_column);
2170
		}
2171
	}
2172
2173
2174
	/********************************************************************************
2175
	 *                               HIDEABLE COLUMNS                               *
2176
	 ********************************************************************************/
2177
2178
2179
	/**
2180
	 * Can datagrid hide colums?
2181
	 * @return boolean
2182
	 */
2183
	public function canHideColumns()
2184
	{
2185
		return (bool) $this->can_hide_columns;
2186
	}
2187
2188
2189
	/**
2190
	 * Order Grid to set columns hideable.
2191
	 * @return static
2192
	 */
2193
	public function setColumnsHideable()
2194
	{
2195
		$this->can_hide_columns = TRUE;
2196
2197
		return $this;
2198
	}
2199
2200
2201
	/********************************************************************************
2202
	 *                                   INTERNAL                                   *
2203
	 ********************************************************************************/
2204
2205
2206
	/**
2207
	 * Get cont of columns
2208
	 * @return int
2209
	 */
2210
	public function getColumnsCount()
2211
	{
2212
		$count = sizeof($this->getColumns());
2213
2214
		if (!empty($this->actions) || $this->isSortable() || $this->getItemsDetail()) {
2215
			$count++;
2216
		}
2217
2218
		if ($this->hasGroupActions()) {
2219
			$count++;
2220
		}
2221
2222
		return $count;
2223
	}
2224
2225
2226
	/**
2227
	 * Get primary key of datagrid data source
2228
	 * @return string
2229
	 */
2230
	public function getPrimaryKey()
2231
	{
2232
		return $this->primary_key;
2233
	}
2234
2235
2236
	/**
2237
	 * Get set of set columns
2238
	 * @return Column\IColumn[]
2239
	 */
2240
	public function getColumns()
2241
	{
2242
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
2243
			if (!empty($this->columns[$column])) {
2244
				$this->columns_visibility[$column] = [
2245
					'visible' => FALSE,
2246
					'name' => $this->columns[$column]->getName()
2247
				];
2248
			}
2249
2250
			$this->removeColumn($column);
2251
		}
2252
2253
		return $this->columns;
2254
	}
2255
2256
2257
	/**
2258
	 * @return PresenterComponent
2259
	 */
2260
	public function getParent()
2261
	{
2262
		$parent = parent::getParent();
2263
2264
		if (!($parent instanceof PresenterComponent)) {
2265
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2266
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2267
			);
2268
		}
2269
2270
		return $parent;
2271
	}
2272
2273
}
2274