Completed
Push — master ( bf6b48...8613c0 )
by Pavel
9s
created

DataGrid::addGroupSelectAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2183
			return $condition;
2184
		}
2185
2186
		return isset($condition[$key]) ? $condition[$key] : FALSE;
2187
	}
2188
2189
2190
	/********************************************************************************
2191
	 *                               COLUMN CALLBACK                                *
2192
	 ********************************************************************************/
2193
2194
2195
	/**
2196
	 * @param  string   $key
2197
	 * @param  callable $callback
2198
	 * @return void
2199
	 */
2200
	public function addColumnCallback($key, callable $callback)
2201
	{
2202
		$this->column_callbacks[$key] = $callback;
2203
	}
2204
2205
2206
	/**
2207
	 * @param  string $key
2208
	 * @return callable|null
2209
	 */
2210
	public function getColumnCallback($key)
2211
	{
2212
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
2213
	}
2214
2215
2216
	/********************************************************************************
2217
	 *                                 INLINE EDIT                                  *
2218
	 ********************************************************************************/
2219
2220
2221
	/**
2222
	 * @return InlineEdit
2223
	 */
2224
	public function addInlineEdit($primary_where_column = NULL)
2225
	{
2226
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
2227
2228
		return $this->inlineEdit;
2229
	}
2230
2231
2232
	/**
2233
	 * @return InlineEdit|null
2234
	 */
2235
	public function getInlineEdit()
2236
	{
2237
		return $this->inlineEdit;
2238
	}
2239
2240
2241
	public function handleInlineEdit($id)
2242
	{
2243
		if ($this->inlineEdit) {
2244
			$this->inlineEdit->setItemId($id);
2245
2246
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
2247
2248
			$this['filter']['inline_edit']->addHidden('_id', $id);
2249
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
2250
2251
			$this->redrawItem($id, $primary_where_column);
2252
		}
2253
	}
2254
2255
2256
	/********************************************************************************
2257
	 *                               HIDEABLE COLUMNS                               *
2258
	 ********************************************************************************/
2259
2260
2261
	/**
2262
	 * Can datagrid hide colums?
2263
	 * @return boolean
2264
	 */
2265
	public function canHideColumns()
2266
	{
2267
		return (bool) $this->can_hide_columns;
2268
	}
2269
2270
2271
	/**
2272
	 * Order Grid to set columns hideable.
2273
	 * @return static
2274
	 */
2275
	public function setColumnsHideable()
2276
	{
2277
		$this->can_hide_columns = TRUE;
2278
2279
		return $this;
2280
	}
2281
2282
2283
	/********************************************************************************
2284
	 *                                   INTERNAL                                   *
2285
	 ********************************************************************************/
2286
2287
2288
	/**
2289
	 * Get cont of columns
2290
	 * @return int
2291
	 */
2292
	public function getColumnsCount()
2293
	{
2294
		$count = sizeof($this->getColumns());
2295
2296
		if (!empty($this->actions) || $this->isSortable() || $this->getItemsDetail() || $this->getInlineEdit()) {
2297
			$count++;
2298
		}
2299
2300
		if ($this->hasGroupActions()) {
2301
			$count++;
2302
		}
2303
2304
		return $count;
2305
	}
2306
2307
2308
	/**
2309
	 * Get primary key of datagrid data source
2310
	 * @return string
2311
	 */
2312
	public function getPrimaryKey()
2313
	{
2314
		return $this->primary_key;
2315
	}
2316
2317
2318
	/**
2319
	 * Get set of set columns
2320
	 * @return Column\IColumn[]
2321
	 */
2322
	public function getColumns()
2323
	{
2324
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
2325
			if (!empty($this->columns[$column])) {
2326
				$this->columns_visibility[$column] = [
2327
					'visible' => FALSE,
2328
					'name' => $this->columns[$column]->getName()
2329
				];
2330
			}
2331
2332
			$this->removeColumn($column);
2333
		}
2334
2335
		return $this->columns;
2336
	}
2337
2338
2339
	/**
2340
	 * @return PresenterComponent
2341
	 */
2342
	public function getParent()
2343
	{
2344
		$parent = parent::getParent();
2345
2346
		if (!($parent instanceof PresenterComponent)) {
2347
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2348
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2349
			);
2350
		}
2351
2352
		return $parent;
2353
	}
2354
2355
}
2356