Completed
Push — master ( e259e3...f37a30 )
by Pavel
02:33
created

DataGrid::getSessionData()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 2 Features 1
Metric Value
c 6
b 2
f 1
dl 0
loc 8
rs 8.8571
cc 5
eloc 4
nc 6
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
18
class DataGrid extends Nette\Application\UI\Control
19
{
20
21
	/**
22
	 * @var callable[]
23
	 */
24
	public $onRedraw;
25
26
	/**
27
	 * @var string
28
	 * @todo Tell about this on github
29
	 */
30
	public static $icon_prefix = 'fa fa-';
31
32
	/**
33
	 * When set to TRUE, datagrid throws an exception
34
	 * 	when tring to get related entity within join and entity does not exist
35
	 * @var bool
36
	 */
37
	public $strict_entity_property = FALSE;
38
39
	/**
40
	 * @var int
41
	 * @persistent
42
	 */
43
	public $page = 1;
44
45
	/**
46
	 * @var int
47
	 * @persistent
48
	 */
49
	public $per_page;
50
51
	/**
52
	 * @var array
53
	 * @persistent
54
	 */
55
	public $sort = [];
56
57
	/**
58
	 * @var array
59
	 * @persistent
60
	 */
61
	public $filter = [];
62
63
	/**
64
	 * @var Callable[]
65
	 */
66
	public $onRender = [];
67
68
	/**
69
	 * @var Callable[]
70
	 */
71
	protected $rowCallback;
72
73
	/**
74
	 * @var array
75
	 */
76
	protected $items_per_page_list;
77
78
	/**
79
	 * @var string
80
	 */
81
	protected $template_file;
82
83
	/**
84
	 * @var Column\IColumn[]
85
	 */
86
	protected $columns = [];
87
88
	/**
89
	 * @var Column\Action[]
90
	 */
91
	protected $actions = [];
92
93
	/**
94
	 * @var GroupAction\GroupActionCollection
95
	 */
96
	protected $group_action_collection;
97
98
	/**
99
	 * @var Filter\Filter[]
100
	 */
101
	protected $filters = [];
102
103
	/**
104
	 * @var Export\Export[]
105
	 */
106
	protected $exports = [];
107
108
	/**
109
	 * @var DataModel
110
	 */
111
	protected $dataModel;
112
113
	/**
114
	 * @var DataFilter
115
	 */
116
	protected $dataFilter;
117
118
	/**
119
	 * @var string
120
	 */
121
	protected $primary_key = 'id';
122
123
	/**
124
	 * @var bool
125
	 */
126
	protected $do_paginate = TRUE;
127
128
	/**
129
	 * @var bool
130
	 */
131
	protected $csv_export = TRUE;
132
133
	/**
134
	 * @var bool
135
	 */
136
	protected $csv_export_filtered = TRUE;
137
138
	/**
139
	 * @var bool
140
	 */
141
	protected $sortable = FALSE;
142
143
	/**
144
	 * @var string
145
	 */
146
	protected $sortable_handler = 'sort!';
147
148
	/**
149
	 * @var string
150
	 */
151
	protected $original_template;
152
153
	/**
154
	 * @var array
155
	 */
156
	protected $redraw_item;
157
158
	/**
159
	 * @var mixed
160
	 */
161
	protected $translator;
162
163
	/**
164
	 * @var bool
165
	 */
166
	protected $force_filter_active;
167
168
	/**
169
	 * @var callable
170
	 */
171
	protected $tree_view_children_callback;
172
173
	/**
174
	 * @var string
175
	 */
176
	protected $tree_view_has_children_column;
177
178
	/**
179
	 * @var bool
180
	 */
181
	protected $outer_filter_rendering = FALSE;
182
183
	/**
184
	 * @var array
185
	 */
186
	protected $columns_export_order = [];
187
188
	/**
189
	 * @var bool
190
	 */
191
	private $remember_state = TRUE;
192
193
	/**
194
	 * @var bool
195
	 */
196
	private $refresh_url = TRUE;
197
198
	/**
199
	 * @var Nette\Http\SessionSection
200
	 */
201
	private $grid_session;
202
203
	/**
204
	 * @var array
205
	 */
206
	private $items_detail = [];
207
208
	/**
209
	 * @var array
210
	 */
211
	private $row_conditions = [
212
		'group_action' => FALSE,
213
		'action' => []
214
	];
215
216
	/**
217
	 * @var bool
218
	 */
219
	protected $can_hide_columns = FALSE;
220
221
222
	/**
223
	 * @param Nette\ComponentModel\IContainer|NULL $parent
224
	 * @param string                               $name
225
	 */
226
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
227
	{
228
		parent::__construct($parent, $name);
229
230
		$this->monitor('Nette\Application\UI\Presenter');
231
	}
232
233
234
	/**
235
	 * {inheritDoc}
236
	 * @return void
237
	 */
238
	public function attached($presenter)
239
	{
240
		parent::attached($presenter);
241
242
		if ($presenter instanceof Nette\Application\UI\Presenter) {
243
			/**
244
			 * Get session
245
			 */
246
			$this->grid_session = $this->getPresenter()->getSession($this->getSessionSectionName());
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 getSession() does only exist in the following implementations of said interface: Nette\Application\UI\Presenter, Nette\Forms\Controls\CsrfProtection.

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...
247
248
			/**
249
			 * Try to find previous filters/pagination/sort in session
250
			 */
251
			$this->findSessionFilters();
252
		}
253
	}
254
255
256
	/********************************************************************************
257
	 *                                  RENDERING                                   *
258
	 ********************************************************************************/
259
260
261
	/**
262
	 * Render template
263
	 * @return void
264
	 */
265
	public function render()
266
	{
267
		/**
268
		 * Check whether datagrid has set some columns, initiated data source, etc
269
		 */
270
		if (!($this->dataModel instanceof DataModel)) {
271
			throw new DataGridException('You have to set a data source first.');
272
		}
273
274
		if (empty($this->columns)) {
275
			throw new DataGridException('You have to add at least one column.');
276
		}
277
278
		$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...
279
280
		/**
281
		 * Invoke some possible events
282
		 */
283
		$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...
284
285
		/**
286
		 * Prepare data for rendering (datagrid may render just one item)
287
		 */
288
		$rows = [];
289
290
		if (!empty($this->redraw_item)) {
291
			$items = $this->dataModel->filterRow($this->redraw_item);
292
		} else {
293
			$items = Nette\Utils\Callback::invokeArgs(
294
				[$this->dataModel, 'filterData'],
295
				[
296
					$this->getPaginator(),
297
					$this->sort,
298
					$this->assableFilters()
299
				]
300
			);
301
		}
302
303
		$callback = $this->rowCallback ?: NULL;
304
305
		foreach ($items as $item) {
306
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
307
308
			if ($callback) {
309
				$callback($item, $row->getControl());
310
			}
311
		}
312
313
		if ($this->isTreeView()) {
314
			$this->template->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...
Bug introduced by
Accessing tree_view_has_children_column on the interface Nette\Application\UI\ITemplate 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...
315
		}
316
317
		$this->template->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...
Bug introduced by
Accessing rows on the interface Nette\Application\UI\ITemplate 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...
318
319
		$this->template->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...
Bug introduced by
Accessing columns on the interface Nette\Application\UI\ITemplate 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...
320
		$this->template->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...
Bug introduced by
Accessing actions on the interface Nette\Application\UI\ITemplate 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...
321
		$this->template->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...
Bug introduced by
Accessing exports on the interface Nette\Application\UI\ITemplate 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...
322
		$this->template->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...
Bug introduced by
Accessing filters on the interface Nette\Application\UI\ITemplate 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...
323
324
		$this->template->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...
Bug introduced by
Accessing filter_active on the interface Nette\Application\UI\ITemplate 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...
325
		$this->template->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...
Bug introduced by
Accessing original_template on the interface Nette\Application\UI\ITemplate 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...
326
		$this->template->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...
Bug introduced by
Accessing icon_prefix on the interface Nette\Application\UI\ITemplate 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...
327
		$this->template->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...
Bug introduced by
Accessing items_detail on the interface Nette\Application\UI\ITemplate 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...
328
329
		/**
330
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
331
		 */
332
		$this->template->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...
Bug introduced by
Accessing filter on the interface Nette\Application\UI\ITemplate 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...
333
334
		/**
335
		 * Set template file and render it
336
		 */
337
		$this->template->setFile($this->getTemplateFile())->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...
Bug introduced by
The method render cannot be called on $this->template->setFile...his->getTemplateFile()) (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
338
	}
339
340
341
	/********************************************************************************
342
	 *                                 ROW CALLBACK                                 *
343
	 ********************************************************************************/
344
345
346
	/**
347
	 * Each row can be modified with user callback
348
	 * @param  callable  $callback
349
	 * @return static
350
	 */
351
	public function setRowCallback(callable $callback)
352
	{
353
		$this->rowCallback = $callback;
0 ignored issues
show
Documentation Bug introduced by
It seems like $callback of type callable is incompatible with the declared type array<integer,callable> of property $rowCallback.

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...
354
355
		return $this;
356
	}
357
358
359
	/********************************************************************************
360
	 *                                 DATA SOURCE                                  *
361
	 ********************************************************************************/
362
363
364
	/**
365
	 * By default ID, you can change that
366
	 * @param string $primary_key
367
	 */
368
	public function setPrimaryKey($primary_key)
369
	{
370
		if ($this->dataModel instanceof DataModel) {
371
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
372
		}
373
374
		$this->primary_key = $primary_key;
375
376
		return $this;
377
	}
378
379
380
	/**
381
	 * Set Grid data source
382
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Kdyby\Doctrine\QueryBuilder $source
383
	 * @return DataGrid
384
	 */
385
	public function setDataSource($source)
386
	{
387
		$this->dataModel = new DataModel($source, $this->primary_key);
388
389
		return $this;
390
	}
391
392
393
	/********************************************************************************
394
	 *                                  TEMPLATING                                  *
395
	 ********************************************************************************/
396
397
398
	/**
399
	 * Set custom template file to render
400
	 * @param string $template_file
401
	 */
402
	public function setTemplateFile($template_file)
403
	{
404
		$this->template_file = $template_file;
405
406
		return $this;
407
	}
408
409
410
	/**
411
	 * Get DataGrid template file
412
	 * @return string
413
	 */
414
	public function getTemplateFile()
415
	{
416
		return $this->template_file ?: $this->getOriginalTemplateFile();
417
	}
418
419
420
	/**
421
	 * Get DataGrid original template file
422
	 * @return string
423
	 */
424
	public function getOriginalTemplateFile()
425
	{
426
		return __DIR__.'/templates/datagrid.latte';
427
	}
428
429
430
	/********************************************************************************
431
	 *                                   SORTING                                    *
432
	 ********************************************************************************/
433
434
435
	/**
436
	 * Set default sorting
437
	 * @param aray $sort
438
	 */
439
	public function setDefaultSort($sort)
440
	{
441
		if (empty($this->sort)) {
442
			$this->sort = (array) $sort;
443
444
			$this->saveSessionData('_grid_sort', $this->sort);
445
		}
446
447
		return $this;
448
	}
449
450
451
	/**
452
	 * Set grido to be sortable
453
	 * @param bool $sortable
454
	 */
455
	public function setSortable($sortable = TRUE)
456
	{
457
		if ($this->getItemsDetail()) {
458
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
459
		}
460
461
		$this->sortable = (bool) $sortable;
462
463
		return $this;
464
	}
465
466
467
	/**
468
	 * Set sortable handle
469
	 * @param string $handle
0 ignored issues
show
Documentation introduced by
There is no parameter named $handle. Did you maybe mean $handler?

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

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

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

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

Loading history...
470
	 */
471
	public function setSortableHandler($handler = 'sort!')
472
	{
473
		$this->sortable_handler = (string) $handler;
474
475
		return $this;
476
	}
477
478
479
	/**
480
	 * Tell whether DataGrid is sortable
481
	 * @return bool
482
	 */
483
	public function isSortable()
484
	{
485
		return $this->sortable;
486
	}
487
488
	/**
489
	 * Return sortable handle name
490
	 * @return string
491
	 */
492
	public function getSortableHandler()
493
	{
494
		return $this->sortable_handler;
495
	}
496
497
498
	/********************************************************************************
499
	 *                                  TREE VIEW                                   *
500
	 ********************************************************************************/
501
502
503
	/**
504
	 * Is tree view set?
505
	 * @return boolean
506
	 */
507
	public function isTreeView()
508
	{
509
		return (bool) $this->tree_view_children_callback;
510
	}
511
512
513
	/**
514
	 * Setting tree view
515
	 * @param callable $get_children_callback
516
	 * @param string $tree_view_has_children_column
517
	 * @return DataGrid
518
	 */
519
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
520
	{
521
		if (!is_callable($get_children_callback)) {
522
			throw new DataGridException(
523
				'Parameters to method DataGrid::setTreeView must be of type callable'
524
			);
525
		}
526
527
		$this->tree_view_children_callback = $get_children_callback;
528
		$this->tree_view_has_children_column = $tree_view_has_children_column;
529
530
		/**
531
		 * TUrn off pagination
532
		 */
533
		$this->setPagination(NULL);
0 ignored issues
show
Documentation introduced by
NULL is of type null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
534
535
		/**
536
		 * Set tree view template file
537
		 */
538
		if (!$this->template_file) {
539
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
540
		}
541
542
		return $this;
543
	}
544
545
546
	/********************************************************************************
547
	 *                                    COLUMNS                                   *
548
	 ********************************************************************************/
549
550
551
	/**
552
	 * Add text column with no other formating
553
	 * @param  string      $key
554
	 * @param  string      $name
555
	 * @param  string|null $column
556
	 * @return Column\ColumnText
557
	 */
558
	public function addColumnText($key, $name, $column = NULL)
559
	{
560
		$this->addColumnCheck($key);
561
		$column = $column ?: $key;
562
563
		return $this->columns[$key] = new Column\ColumnText($column, $name);
564
	}
565
566
567
	/**
568
	 * Add column with link
569
	 * @param  string      $key
570
	 * @param  string      $name
571
	 * @param  string|null $column
572
	 * @return Column\ColumnLink
573
	 */
574
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
575
	{
576
		$this->addColumnCheck($key);
577
		$column = $column ?: $key;
578
		$href = $href ?: $key;
579
580
		if (NULL === $params) {
581
			$params = [$this->primary_key];
582
		}
583
584
		return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params);
585
	}
586
587
588
	/**
589
	 * Add column with possible number formating
590
	 * @param  string      $key
591
	 * @param  string      $name
592
	 * @param  string|null $column
593
	 * @return Column\ColumnNumber
594
	 */
595
	public function addColumnNumber($key, $name, $column = NULL)
596
	{
597
		$this->addColumnCheck($key);
598
		$column = $column ?: $key;
599
600
		return $this->columns[$key] = new Column\ColumnNumber($column, $name);
601
	}
602
603
604
	/**
605
	 * Add column with date formating
606
	 * @param  string      $key
607
	 * @param  string      $name
608
	 * @param  string|null $column
609
	 * @return Column\ColumnDateTime
610
	 */
611
	public function addColumnDateTime($key, $name, $column = NULL)
612
	{
613
		$this->addColumnCheck($key);
614
		$column = $column ?: $key;
615
616
		return $this->columns[$key] = new Column\ColumnDateTime($column, $name);
617
	}
618
619
620
	/**
621
	 * Return existing column
622
	 * @param  string $key
623
	 * @return Column\Column
624
	 * @throws DataGridException
625
	 */
626
	public function getColumn($key)
627
	{
628
		if (!isset($this->columns[$key])) {
629
			throw new DataGridException("There is no column at key [$key] defined.");
630
		}
631
632
		return $this->columns[$key];
633
	}
634
635
636
	/**
637
	 * Remove column
638
	 * @param string $key
639
	 * @return void
640
	 */
641
	public function removeColumn($key)
642
	{
643
		unset($this->columns[$key]);
644
	}
645
646
647
	/**
648
	 * Check whether given key already exists in $this->columns
649
	 * @param  string $key
650
	 * @throws DataGridException
651
	 */
652
	protected function addColumnCheck($key)
653
	{
654
		if (isset($this->columns[$key])) {
655
			throw new DataGridException("There is already column at key [$key] defined.");
656
		}
657
	}
658
659
660
	/********************************************************************************
661
	 *                                    ACTIONS                                   *
662
	 ********************************************************************************/
663
664
665
	/**
666
	 * Create action
667
	 * @param string     $key
668
	 * @param string     $name
669
	 * @param string     $href
670
	 * @param array|null $params
671
	 * @return Column\Action
672
	 */
673
	public function addAction($key, $name, $href = NULL, array $params = NULL)
674
	{
675
		$this->addActionCheck($key);
676
		$href = $href ?: $key;
677
678
		if (NULL === $params) {
679
			$params = [$this->primary_key];
680
		}
681
682
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
683
	}
684
685
686
	/**
687
	 * Create action callback
688
	 * @param string     $key
689
	 * @param string     $name
690
	 * @return Column\Action
691
	 */
692
	public function addActionCallback($key, $name)
693
	{
694
		$this->addActionCheck($key);
695
		$params = ['__id' => $this->primary_key];
696
697
		return $this->actions[$key] = new Column\ActionCallback($this, $key, $name, $params);
698
	}
699
700
701
	/**
702
	 * Get existing action
703
	 * @param  string       $key
704
	 * @return Column\Action
705
	 * @throws DataGridException
706
	 */
707
	public function getAction($key)
708
	{
709
		if (!isset($this->actions[$key])) {
710
			throw new DataGridException("There is no action at key [$key] defined.");
711
		}
712
713
		return $this->actions[$key];
714
	}
715
716
717
	/**
718
	 * Remove action
719
	 * @param string $key
720
	 * @return void
721
	 */
722
	public function removeAction($key)
723
	{
724
		unset($this->actions[$key]);
725
	}
726
727
728
	/**
729
	 * Check whether given key already exists in $this->filters
730
	 * @param  string $key
731
	 * @throws DataGridException
732
	 */
733
	protected function addActionCheck($key)
734
	{
735
		if (isset($this->actions[$key])) {
736
			throw new DataGridException("There is already action at key [$key] defined.");
737
		}
738
	}
739
740
741
	/********************************************************************************
742
	 *                                    FILTERS                                   *
743
	 ********************************************************************************/
744
745
746
	/**
747
	 * Add filter fot text search
748
	 * @param string       $key
749
	 * @param string       $name
750
	 * @param array|string $columns
751
	 * @return Filter\FilterText
752
	 * @throws DataGridException
753
	 */
754
	public function addFilterText($key, $name, $columns = NULL)
755
	{
756
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
757
758
		if (!is_array($columns)) {
759
			throw new DataGridException("Filter Text can except only array or string.");
760
		}
761
762
		$this->addFilterCheck($key);
763
764
		return $this->filters[$key] = new Filter\FilterText($key, $name, $columns);
0 ignored issues
show
Documentation introduced by
$columns is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
765
	}
766
767
768
	/**
769
	 * Add select box filter
770
	 * @param string $key
771
	 * @param string $name
772
	 * @param array  $options
773
	 * @param string $column
774
	 * @return Filter\FilterSelect
775
	 * @throws DataGridException
776
	 */
777 View Code Duplication
	public function addFilterSelect($key, $name, $options, $column = NULL)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
778
	{
779
		$column = $column ?: $key;
780
781
		if (!is_string($column)) {
782
			throw new DataGridException("Filter Select can only filter through one column.");
783
		}
784
785
		$this->addFilterCheck($key);
786
787
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
788
	}
789
790
791
	/**
792
	 * Add datepicker filter
793
	 * @param string $key
794
	 * @param string $name
795
	 * @param string $column
796
	 * @return Filter\FilterDate
797
	 * @throws DataGridException
798
	 */
799
	public function addFilterDate($key, $name, $column = NULL)
800
	{
801
		$column = $column ?: $key;
802
803
		if (!is_string($column)) {
804
			throw new DataGridException("FilterDate can only filter through one column.");
805
		}
806
807
		$this->addFilterCheck($key);
808
809
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
810
	}
811
812
813
	/**
814
	 * Add range filter (from - to)
815
	 * @param string $key
816
	 * @param string $name
817
	 * @param string $column
818
	 * @return Filter\FilterRange
819
	 * @throws DataGridException
820
	 */
821 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...
822
	{
823
		$column = $column ?: $key;
824
825
		if (!is_string($column)) {
826
			throw new DataGridException("FilterRange can only filter through one column.");
827
		}
828
829
		$this->addFilterCheck($key);
830
831
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
832
	}
833
834
835
	/**
836
	 * Add datepicker filter (from - to)
837
	 * @param string $key
838
	 * @param string $name
839
	 * @param string $column
840
	 * @return Filter\FilterDateRange
841
	 * @throws DataGridException
842
	 */
843 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...
844
	{
845
		$column = $column ?: $key;
846
847
		if (!is_string($column)) {
848
			throw new DataGridException("FilterDateRange can only filter through one column.");
849
		}
850
851
		$this->addFilterCheck($key);
852
853
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
854
	}
855
856
857
	/**
858
	 * Check whether given key already exists in $this->filters
859
	 * @param  string $key
860
	 * @throws DataGridException
861
	 */
862
	protected function addFilterCheck($key)
863
	{
864
		if (isset($this->filters[$key])) {
865
			throw new DataGridException("There is already action at key [$key] defined.");
866
		}
867
	}
868
869
870
	/**
871
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
872
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
873
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
874
	 */
875
	public function assableFilters()
876
	{
877
		foreach ($this->filter as $key => $value) {
878
			if (!isset($this->filters[$key])) {
879
				$this->deleteSesssionData($key);
880
881
				continue;
882
			}
883
884
			if (is_array($value) || $value instanceof \Traversable) {
885
				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...
886
					$this->filters[$key]->setValue($value);
887
				}
888
			} else {
889
				if ($value !== '' && $value !== NULL) {
890
					$this->filters[$key]->setValue($value);
891
				}
892
			}
893
		}
894
895
		foreach ($this->columns as $column) {
896
			if (isset($this->sort[$column->getColumnName()])) {
897
				$column->setSort($this->sort);
898
			}
899
		}
900
901
		return $this->filters;
902
	}
903
904
905
	/**
906
	 * Remove filter
907
	 * @param string $key
908
	 * @return void
909
	 */
910
	public function removeFilter($key)
911
	{
912
		unset($this->filters[$key]);
913
	}
914
915
916
	/********************************************************************************
917
	 *                                  FILTERING                                   *
918
	 ********************************************************************************/
919
920
921
	/**
922
	 * Is filter active?
923
	 * @return boolean
924
	 */
925
	public function isFilterActive()
926
	{
927
		$is_filter = ArraysHelper::testTruthy($this->filter);
928
929
		return ($is_filter) || $this->force_filter_active;
930
	}
931
932
933
	/**
934
	 * Tell that filter is active from whatever reasons
935
	 * return static
936
	 */
937
	public function setFilterActive()
938
	{
939
		$this->force_filter_active = TRUE;
940
941
		return $this;
942
	}
943
944
945
	/**
946
	 * If we want to sent some initial filter
947
	 * @param array $filter
948
	 * @return static
949
	 */
950
	public function setFilter(array $filter)
951
	{
952
		$this->filter = $filter;
953
954
		return $this;
955
	}
956
957
958
	/**
959
	 * FilterAndGroupAction form factory
960
	 * @return Form
961
	 */
962
	public function createComponentFilter()
963
	{
964
		$form = new Form($this, 'filter');
965
966
		$form->setMethod('get');
967
968
		/**
969
		 * Filter part
970
		 */
971
		$filter_container = $form->addContainer('filter');
972
973
		foreach ($this->filters as $filter) {
974
			$filter->addToFormContainer($filter_container, $filter_container);
0 ignored issues
show
Documentation Bug introduced by
The method addToFormContainer does not exist on object<Ublaboo\DataGrid\Filter\Filter>? 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...
975
		}
976
977
		/**
978
		 * Group action part
979
		 */
980
		$group_action_container = $form->addContainer('group_action');
981
982
		if ($this->hasGroupActions()) {
983
			$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator());
984
		}
985
986
		$form->setDefaults(['filter' => $this->filter]);
987
988
		$form->onSubmit[] = [$this, 'filterSucceeded'];
989
990
		return $form;
991
	}
992
993
994
	/**
995
	 * Set $this->filter values after filter form submitted
996
	 * @param  Form $form
997
	 * @return void
998
	 */
999
	public function filterSucceeded(Form $form)
1000
	{
1001
		$values = $form->getValues();
1002
1003
		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...
1004
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1005
				return;
1006
			}
1007
		}
1008
1009
		$values = $values['filter'];
1010
1011
		foreach ($values as $key => $value) {
1012
			/**
1013
			 * Session stuff
1014
			 */
1015
			$this->saveSessionData($key, $value);
1016
1017
			/**
1018
			 * Other stuff
1019
			 */
1020
			$this->filter[$key] = $value;
1021
		}
1022
1023
		$this->reload();
1024
	}
1025
1026
1027
	/**
1028
	 * Should be datagrid filters rendered separately?
1029
	 * @param boolean $out
1030
	 * @return static
1031
	 */
1032
	public function setOuterFilterRendering($out = TRUE)
1033
	{
1034
		$this->outer_filter_rendering = (bool) $out;
1035
1036
		return $this;
1037
	}
1038
1039
1040
	/**
1041
	 * Are datagrid filters rendered separately?
1042
	 * @return boolean
1043
	 */
1044
	public function hasOuterFilterRendering()
1045
	{
1046
		return $this->outer_filter_rendering;
1047
	}
1048
1049
1050
	/**
1051
	 * Try to restore session stuff
1052
	 * @return void
1053
	 */
1054
	public function findSessionFilters()
1055
	{
1056
		if ($this->filter || ($this->page != 1) || $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...
Bug Best Practice introduced by
The expression $this->sort 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...
1057
			return;
1058
		}
1059
1060
		if (!$this->remember_state) {
1061
			return;
1062
		}
1063
1064
		if ($page = $this->getSessionData('_grid_page')) {
1065
			$this->page = $page;
1066
		}
1067
1068
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1069
			$this->per_page = $per_page;
1070
		}
1071
1072
		if ($sort = $this->getSessionData('_grid_sort')) {
1073
			$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...
1074
		}
1075
1076
		foreach ($this->getSessionData() as $key => $value) {
1077
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1078
				$this->filter[$key] = $value;
1079
			}
1080
		}
1081
	}
1082
1083
1084
	/********************************************************************************
1085
	 *                                    EXPORTS                                   *
1086
	 ********************************************************************************/
1087
1088
1089
	/**
1090
	 * Add export of type callback
1091
	 * @param string $text
1092
	 * @param callable $callback
1093
	 * @param boolean $filtered
1094
	 * @return Export\Export
1095
	 */
1096
	public function addExportCallback($text, $callback, $filtered = FALSE)
1097
	{
1098
		if (!is_callable($callback)) {
1099
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1100
		}
1101
1102
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1103
	}
1104
1105
1106
	/**
1107
	 * Add already implemented csv export
1108
	 * @param string $text
1109
	 * @param string $csv_file_name
1110
	 * @return Export\Export
1111
	 */
1112
	public function addExportCsv($text, $csv_file_name)
1113
	{
1114
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1115
	}
1116
1117
1118
	/**
1119
	 * Add already implemented csv export, but for filtered data
1120
	 * @param string $text
1121
	 * @param string $csv_file_name
1122
	 * @return Export\Export
1123
	 */
1124
	public function addExportCsvFiltered($text, $csv_file_name)
1125
	{
1126
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1127
	}
1128
1129
1130
	/**
1131
	 * Add export to array
1132
	 * @param Export\Export $export
1133
	 * @return Export\Export
1134
	 */
1135
	protected function addToExports(Export\Export $export)
1136
	{
1137
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1138
1139
		$export->setLink($this->link('export!', ['id' => $id]));
1140
1141
		return $this->exports[$id] = $export;
1142
	}
1143
1144
1145
	public function resetExportsLinks()
1146
	{
1147
		foreach ($this->exports as $id => $export) {
1148
			$export->setLink($this->link('export!', ['id' => $id]));
1149
		}
1150
	}
1151
1152
1153
	/********************************************************************************
1154
	 *                                 GROUP ACTIONS                                *
1155
	 ********************************************************************************/
1156
1157
1158
	/**
1159
	 * Add group actino
1160
	 * @param string $title
1161
	 * @param array  $options
1162
	 * @return GroupAction\GroupAction
1163
	 */
1164
	public function addGroupAction($title, $options = [])
1165
	{
1166
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1167
	}
1168
1169
1170
	/**
1171
	 * Get collection of all group actions
1172
	 * @return GroupAction\GroupActionCollection
1173
	 */
1174
	public function getGroupActionCollection()
1175
	{
1176
		if (!$this->group_action_collection) {
1177
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1178
		}
1179
1180
		return $this->group_action_collection;
1181
	}
1182
1183
1184
	/**
1185
	 * Has datagrid some group actions?
1186
	 * @return boolean
1187
	 */
1188
	public function hasGroupActions()
1189
	{
1190
		return (bool) $this->group_action_collection;
1191
	}
1192
1193
1194
	/********************************************************************************
1195
	 *                                   HANDLERS                                   *
1196
	 ********************************************************************************/
1197
1198
1199
	/**
1200
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1201
	 * @param  int  $page
1202
	 * @return void
1203
	 */
1204
	public function handlePage($page)
1205
	{
1206
		/**
1207
		 * Session stuff
1208
		 */
1209
		$this->page = $page;
1210
		$this->saveSessionData('_grid_page', $page);
1211
1212
		$this->reload(['table']);
1213
	}
1214
1215
1216
	/**
1217
	 * Handler for sorting
1218
	 * @param array $sort
1219
	 * @return void
1220
	 */
1221
	public function handleSort(array $sort)
1222
	{
1223
		/**
1224
		 * Session stuff
1225
		 */
1226
		$this->sort = $sort;
1227
		$this->saveSessionData('_grid_sort', $this->sort);
1228
1229
		$this->reload(['table']);
1230
	}
1231
1232
1233
	/**
1234
	 * handler for reseting the filter
1235
	 * @return void
1236
	 */
1237
	public function handleResetFilter()
1238
	{
1239
		/**
1240
		 * Session stuff
1241
		 */
1242
		$this->deleteSesssionData('_grid_page');
1243
1244
		foreach ($this->getSessionData() as $key => $value) {
1245
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1246
				$this->deleteSesssionData($key);
1247
			}
1248
		}
1249
1250
		$this->filter = [];
1251
1252
		$this->reload(['grid']);
1253
	}
1254
1255
1256
	/**
1257
	 * Handler for export
1258
	 * @param  int $id Key for particular export class in array $this->exports
1259
	 * @return void
1260
	 */
1261
	public function handleExport($id)
1262
	{
1263
		if (!isset($this->exports[$id])) {
1264
			throw new Nette\Application\ForbiddenRequestException;
1265
		}
1266
1267
		if ($this->columns_export_order) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->columns_export_order 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...
1268
			$this->setColumnsOrder($this->columns_export_order);
1269
		}
1270
1271
		$export = $this->exports[$id];
1272
1273
		if ($export->isFiltered()) {
1274
			$sort      = $this->sort;
1275
			$filter    = $this->assableFilters();
1276
		} else {
1277
			$sort      = $this->primary_key;
1278
			$filter    = [];
1279
		}
1280
1281
		if (NULL === $this->dataModel) {
1282
			throw new DataGridException('You have to set a data source first.');
1283
		}
1284
1285
		$rows = [];
1286
1287
		$items = Nette\Utils\Callback::invokeArgs(
1288
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1289
		);
1290
1291
		foreach ($items as $item) {
1292
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1293
		}
1294
1295
		if ($export instanceof Export\ExportCsv) {
1296
			$export->invoke($rows, $this);
1297
		} else {
1298
			$export->invoke($items, $this);
1299
		}
1300
1301
		if ($export->isAjax()) {
1302
			$this->reload();
1303
		}
1304
	}
1305
1306
1307
	/**
1308
	 * Handler for getting children of parent item (e.g. category)
1309
	 * @param  int $parent
1310
	 * @return void
1311
	 */
1312 View Code Duplication
	public function handleGetChildren($parent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1313
	{
1314
		$this->setDataSource(
1315
			call_user_func($this->tree_view_children_callback, $parent)
1316
		);
1317
1318
		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...
1319
			$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...
1320
			$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...
1321
1322
			$this->redrawControl('items');
1323
1324
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1325
		} else {
1326
			$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...
1327
		}
1328
	}
1329
1330
1331
	/**
1332
	 * Handler for getting item detail
1333
	 * @param  mixed $id
1334
	 * @return void
1335
	 */
1336 View Code Duplication
	public function handleGetItemDetail($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1337
	{
1338
		$this->template->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...
Bug introduced by
Accessing toggle_detail on the interface Nette\Application\UI\ITemplate 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...
1339
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
0 ignored issues
show
Bug introduced by
The method getPrimaryWhereColumn cannot be called on $this->items_detail (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1340
1341
		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...
1342
			$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...
1343
			$this->redrawControl('items');
1344
1345
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1346
		} else {
1347
			$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...
1348
		}
1349
	}
1350
1351
1352
	/**
1353
	 * Handler for inline editing
1354
	 * @param  mixed $id
1355
	 * @param  mixed $key
1356
	 * @return void
1357
	 */
1358
	public function handleEdit($id, $key)
1359
	{
1360
		$column = $this->getColumn($key);
1361
		$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...
1362
1363
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1364
	}
1365
1366
1367
	/**
1368
	 * Redraw $this
1369
	 * @return void
1370
	 */
1371
	public function reload($snippets = [])
1372
	{
1373
		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...
1374
			$this->redrawControl('tbody');
1375
			$this->redrawControl('pagination');
1376
1377
			/**
1378
			 * manualy reset exports links...
1379
			 */
1380
			$this->resetExportsLinks();
1381
			$this->redrawControl('exports');
1382
1383
			foreach ($snippets as $snippet) {
1384
				$this->redrawControl($snippet);
1385
			}
1386
1387
			$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...
1388
1389
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1390
		} else {
1391
			$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...
1392
		}
1393
	}
1394
1395
1396
	/**
1397
	 * Redraw just one row via ajax
1398
	 * @param  int   $id
1399
	 * @param  mixed $primary_where_column
1400
	 * @return void
1401
	 */
1402
	public function redrawItem($id, $primary_where_column = NULL)
1403
	{
1404
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1405
1406
		$this->redrawControl('items');
1407
		$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...
1408
1409
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1410
	}
1411
1412
1413
	/**
1414
	 * Tell datagrid to display all columns
1415
	 * @return void
1416
	 */
1417
	public function handleShowAllColumns()
1418
	{
1419
		$this->deleteSesssionData('_grid_hidden_columns');
1420
1421
		$this->redrawControl();
1422
1423
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1424
	}
1425
1426
1427
	/**
1428
	 * Notice datagrid to not display particular columns
1429
	 * @param  string $column
1430
	 * @return void
1431
	 */
1432
	public function handleHideColumn($column)
1433
	{
1434
		/**
1435
		 * Store info about hiding a column to session
1436
		 */
1437
		$columns = $this->getSessionData('_grid_hidden_columns');
1438
1439
		if (empty($columns)) {
1440
			$columns = [$column];
1441
		} else if (!in_array($column, $columns)) {
1442
			array_push($columns, $column);
1443
		}
1444
1445
		$this->saveSessionData('_grid_hidden_columns', $columns);
1446
1447
		$this->redrawControl();
1448
1449
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1450
	}
1451
1452
1453
	public function handleActionCallback($__key, $__id)
1454
	{
1455
		$action = $this->getAction($__key);
1456
1457
		if (!($action instanceof Column\ActionCallback)) {
1458
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1459
		}
1460
1461
		$action->onClick($__id);
0 ignored issues
show
Documentation Bug introduced by
The method onClick does not exist on object<Ublaboo\DataGrid\Column\ActionCallback>? 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...
1462
	}
1463
1464
1465
	/********************************************************************************
1466
	 *                                  PAGINATION                                  *
1467
	 ********************************************************************************/
1468
1469
1470
	/**
1471
	 * Set options of select "items_per_page"
1472
	 * @param array $items_per_page_list
1473
	 */
1474
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1475
	{
1476
		$this->items_per_page_list = $items_per_page_list;
1477
1478
		if ($include_all) {
1479
			$this->items_per_page_list[] = 'all';
1480
		}
1481
1482
		return $this;
1483
	}
1484
1485
1486
	/**
1487
	 * Paginator factory
1488
	 * @return Components\DataGridPaginator\DataGridPaginator
1489
	 */
1490
	public function createComponentPaginator()
1491
	{
1492
		/**
1493
		 * Init paginator
1494
		 */
1495
		$component = new Components\DataGridPaginator\DataGridPaginator;
1496
		$paginator = $component->getPaginator();
1497
1498
		$paginator->setPage($this->page);
1499
		$paginator->setItemsPerPage($this->getPerPage());
1500
1501
		return $component;
1502
	}
1503
1504
1505
	/**
1506
	 * PerPage form factory
1507
	 * @return Form
1508
	 */
1509
	public function createComponentPerPage()
1510
	{
1511
		$form = new Form;
1512
1513
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1514
			->setValue($this->getPerPage());
1515
1516
		$form->addSubmit('submit', '');
1517
1518
		$saveSessionData = [$this, 'saveSessionData'];
1519
1520
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1521
			/**
1522
			 * Session stuff
1523
			 */
1524
			$saveSessionData('_grid_per_page', $values->per_page);
1525
1526
			/**
1527
			 * Other stuff
1528
			 */
1529
			$this->per_page = $values->per_page;
1530
			$this->reload();
1531
		};
1532
1533
		return $form;
1534
	}
1535
1536
1537
	/**
1538
	 * Get parameter per_page
1539
	 * @return int
1540
	 */
1541
	public function getPerPage()
1542
	{
1543
		$items_per_page_list = $this->getItemsPerPageList();
1544
1545
		$per_page = $this->per_page ?: reset($items_per_page_list);
1546
1547
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
1548
			$per_page = reset($items_per_page_list);
1549
		}
1550
1551
		return $per_page;
1552
	}
1553
1554
1555
	/**
1556
	 * Get associative array of items_per_page_list
1557
	 * @return array
1558
	 */
1559
	public function getItemsPerPageList()
1560
	{
1561
		if (!$this->items_per_page_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->items_per_page_list 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...
1562
			$this->setItemsPerPageList([10, 20, 50], TRUE);
1563
		}
1564
1565
		$list = array_flip($this->items_per_page_list);
1566
1567
		foreach ($list as $key => $value) {
1568
			$list[$key] = $key;
1569
		}
1570
1571
		if (array_key_exists('all', $list)) {
1572
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
1573
		}
1574
1575
		return $list;
1576
	}
1577
1578
1579
	/**
1580
	 * Order Grid to "be paginated"
1581
	 * @param bool $do
1582
	 * @return static
1583
	 */
1584
	public function setPagination($do)
1585
	{
1586
		$this->do_paginate = (bool) $do;
1587
1588
		return $this;
1589
	}
1590
1591
1592
	/**
1593
	 * Tell whether Grid is paginated
1594
	 * @return bool
1595
	 */
1596
	public function isPaginated()
1597
	{
1598
		return $this->do_paginate;
1599
	}
1600
1601
1602
	/**
1603
	 * Return current paginator class
1604
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1605
	 */
1606
	public function getPaginator()
1607
	{
1608
		if ($this->isPaginated() && $this->per_page !== 'all') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $this->per_page (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
1609
			return $this['paginator'];
1610
		}
1611
1612
		return NULL;
1613
	}
1614
1615
1616
	/********************************************************************************
1617
	 *                                     I18N                                     *
1618
	 ********************************************************************************/
1619
1620
1621
	/**
1622
	 * Set datagrid translator
1623
	 * @param Nette\Localization\ITranslator $translator
1624
	 * @return static
1625
	 */
1626
	public function setTranslator(Nette\Localization\ITranslator $translator)
1627
	{
1628
		$this->translator = $translator;
1629
1630
		return $this;
1631
	}
1632
1633
1634
	/**
1635
	 * Get translator for datagrid
1636
	 * @return Nette\Localization\ITranslator
1637
	 */
1638
	public function getTranslator()
1639
	{
1640
		if (!$this->translator) {
1641
			$this->translator = new Localization\SimpleTranslator;
1642
		}
1643
1644
		return $this->translator;
1645
	}
1646
1647
1648
	/********************************************************************************
1649
	 *                                 COLUMNS ORDER                                *
1650
	 ********************************************************************************/
1651
1652
1653
	/**
1654
	 * Set order of datagrid columns
1655
	 * @param array $order
1656
	 * @return static
1657
	 */
1658
	public function setColumnsOrder($order)
1659
	{
1660
		$new_order = [];
1661
1662
		foreach ($order as $key) {
1663
			if (isset($this->columns[$key])) {
1664
				$new_order[$key] = $this->columns[$key];
1665
			}
1666
		}
1667
1668
		if (sizeof($new_order) === sizeof($this->columns)) {
1669
			$this->columns = $new_order;
1670
		} else {
1671
			throw new DataGridException('When changing columns order, you have to specify all columns');
1672
		}
1673
1674
		return $this;
1675
	}
1676
1677
1678
	/**
1679
	 * Columns order may be different for export and normal grid
1680
	 * @param array $order
1681
	 */
1682
	public function setColumnsExportOrder($order)
1683
	{
1684
		$this->columns_export_order = (array) $order;
1685
	}
1686
1687
1688
	/********************************************************************************
1689
	 *                                SESSION & URL                                 *
1690
	 ********************************************************************************/
1691
1692
1693
	/**
1694
	 * Find some unique session key name
1695
	 * @return string
1696
	 */
1697
	public function getSessionSectionName()
1698
	{
1699
		return $this->getPresenter()->getName().':'.$this->getName();
1700
	}
1701
1702
1703
	/**
1704
	 * Should datagrid remember its filters/pagination/etc using session?
1705
	 * @param bool $remember
1706
	 * @return static
1707
	 */
1708
	public function setRememberState($remember = TRUE)
1709
	{
1710
		$this->remember_state = (bool) $remember;
1711
1712
		return $this;
1713
	}
1714
1715
1716
	/**
1717
	 * Should datagrid refresh url using history API?
1718
	 * @param bool $refresh
1719
	 * @return static
1720
	 */
1721
	public function setRefreshUrl($refresh = TRUE)
1722
	{
1723
		$this->refresh_url = (bool) $refresh;
1724
1725
1726
		return $this;
1727
	}
1728
1729
1730
	/**
1731
	 * Get session data if functionality is enabled
1732
	 * @param  string $key
1733
	 * @return mixed
1734
	 */
1735
	public function getSessionData($key = NULL, $default_value = NULL)
1736
	{
1737
		if (!$this->remember_state) {
1738
			return $key ? $default_value : [];
1739
		}
1740
1741
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
1742
	}
1743
1744
1745
	/**
1746
	 * Save session data - just if it is enabled
1747
	 * @param  string $key
1748
	 * @param  mixed  $value
1749
	 * @return void
1750
	 */
1751
	public function saveSessionData($key, $value)
1752
	{
1753
		if ($this->remember_state) {
1754
			$this->grid_session->{$key} = $value;
1755
		}
1756
	}
1757
1758
1759
	/**
1760
	 * Delete session data
1761
	 * @return void
1762
	 */
1763
	public function deleteSesssionData($key)
1764
	{
1765
		unset($this->grid_session->{$key});
1766
	}
1767
1768
1769
	/********************************************************************************
1770
	 *                                  ITEM DETAIL                                 *
1771
	 ********************************************************************************/
1772
1773
1774
	/**
1775
	 * Get items detail parameters
1776
	 * @return array
1777
	 */
1778
	public function getItemsDetail()
1779
	{
1780
		return $this->items_detail;
1781
	}
1782
1783
1784
	/**
1785
	 * Items can have thair detail - toggled
1786
	 * @param mixed $detail callable|string|bool
1787
	 * @param bool|NULL $primary_where_column
1788
	 * @return Column\ItemDetail
1789
	 */
1790
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1791
	{
1792
		if ($this->isSortable()) {
1793
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1794
		}
1795
1796
		$this->items_detail = new Column\ItemDetail(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Ublaboo\DataGrid\Co... ?: $this->primary_key) of type object<Ublaboo\DataGrid\Column\ItemDetail> is incompatible with the declared type array of property $items_detail.

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...
1797
			$this,
1798
			$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...
1799
		);
1800
1801
		if (is_string($detail)) {
1802
			/**
1803
			 * Item detail will be in separate template
1804
			 */
1805
			$this->items_detail->setType('template');
1806
			$this->items_detail->setTemplate($detail);
1807
1808
		} else if (is_callable($detail)) {
1809
			/**
1810
			 * Item detail will be rendered via custom callback renderer
1811
			 */
1812
			$this->items_detail->setType('renderer');
1813
			$this->items_detail->setRenderer($detail);
1814
1815
		} else if (TRUE === $detail) {
1816
			/**
1817
			 * Item detail will be rendered probably via block #detail
1818
			 */
1819
			$this->items_detail->setType('block');
1820
1821
		} else {
1822
			throw new DataGridException(
1823
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1824
			);
1825
		}
1826
1827
		return $this->items_detail;
1828
	}
1829
1830
1831
	/********************************************************************************
1832
	 *                                ROW PRIVILEGES                                *
1833
	 ********************************************************************************/
1834
1835
1836
	public function allowRowsGroupAction(callable $condition)
1837
	{
1838
		$this->row_conditions['group_action'] = $condition;
1839
	}
1840
1841
1842
	public function allowRowsAction($key, callable $condition)
1843
	{
1844
		$this->row_conditions['action'][$key] = $condition;
1845
	}
1846
1847
1848
	public function getRowCondition($name, $key = NULL)
1849
	{
1850
		if (!isset($this->row_conditions[$name])) {
1851
			return FALSE;
1852
		}
1853
1854
		$condition = $this->row_conditions[$name];
1855
1856
		if (!$key) {
1857
			return $condition;
1858
		}
1859
1860
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1861
	}
1862
1863
1864
	/********************************************************************************
1865
	 *                               HIDEABLE COLUMNS                               *
1866
	 ********************************************************************************/
1867
1868
1869
	/**
1870
	 * Can datagrid hide colums?
1871
	 * @return boolean
1872
	 */
1873
	public function canHideColumns()
1874
	{
1875
		return (bool) $this->can_hide_columns;
1876
	}
1877
1878
1879
	/**
1880
	 * Order Grid to set columns hideable.
1881
	 * @param bool $do
0 ignored issues
show
Bug introduced by
There is no parameter named $do. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
1882
	 * @return static
1883
	 */
1884
	public function setColumnsHideable()
1885
	{
1886
		$this->can_hide_columns = TRUE;
1887
1888
		return $this;
1889
	}
1890
1891
1892
	/********************************************************************************
1893
	 *                                   INTERNAL                                   *
1894
	 ********************************************************************************/
1895
1896
1897
	/**
1898
	 * Get cont of columns
1899
	 * @return int
1900
	 */
1901
	public function getColumnsCount()
1902
	{
1903
		$count = sizeof($this->getColumns());
1904
1905
		if ($this->actions || $this->isSortable() || $this->getItemsDetail()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->actions of type Ublaboo\DataGrid\Column\Action[] 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...
1906
			$count++;
1907
		}
1908
1909
		if ($this->hasGroupActions()) {
1910
			$count++;
1911
		}
1912
1913
		return $count;
1914
	}
1915
1916
1917
	/**
1918
	 * Get primary key of datagrid data source
1919
	 * @return string
1920
	 */
1921
	public function getPrimaryKey()
1922
	{
1923
		return $this->primary_key;
1924
	}
1925
1926
1927
	/**
1928
	 * Get set of set columns
1929
	 * @return Column\IColumn[]
1930
	 */
1931
	public function getColumns()
1932
	{
1933
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
1934
			$this->removeColumn($column);
1935
		}
1936
1937
		return $this->columns;
1938
	}
1939
1940
1941
1942
	/**
1943
	 * @return PresenterComponent
1944
	 */
1945
	public function getParent()
1946
	{
1947
		$parent = parent::getParent();
1948
		if (!$parent instanceof PresenterComponent) {
1949
			throw new DataGridHasToBeAttachedToPresenterComponentException(
1950
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
1951
			);
1952
		}
1953
1954
		return $parent;
1955
	}
1956
1957
}
1958