Completed
Pull Request — master (#73)
by Martin
02:48
created

DataGrid::setRowCallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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 Ublaboo\DataGrid\Utils\ArraysHelper;
13
use Nette\Application\UI\Form;
14
15
class DataGrid extends Nette\Application\UI\Control
16
{
17
18
	/**
19
	 * @var callable[]
20
	 */
21
	public $onRedraw;
22
23
	/**
24
	 * @var string
25
	 * @todo Tell about this on github
26
	 */
27
	public static $icon_prefix = 'fa fa-';
28
29
	/**
30
	 * When set to TRUE, datagrid throws an exception
31
	 * 	when tring to get related entity within join and entity does not exist
32
	 * @var bool
33
	 */
34
	public $strict_entity_property = FALSE;
35
36
	/**
37
	 * @var int
38
	 * @persistent
39
	 */
40
	public $page = 1;
41
42
	/**
43
	 * @var int
44
	 * @persistent
45
	 */
46
	public $per_page;
47
48
	/**
49
	 * @var array
50
	 * @persistent
51
	 */
52
	public $sort = [];
53
54
	/**
55
	 * @var array
56
	 * @persistent
57
	 */
58
	public $filter = [];
59
60
	/**
61
	 * @var Callable[]
62
	 */
63
	public $onRender = [];
64
65
	/**
66
	 * @var Callable[]
67
	 */
68
	protected $rowCallback;
69
70
	/**
71
	 * @var array
72
	 */
73
	protected $items_per_page_list = [10, 20, 50];
74
75
	/**
76
	 * @var string
77
	 */
78
	protected $template_file;
79
80
	/**
81
	 * @var Column\IColumn[]
82
	 */
83
	protected $columns = [];
84
85
	/**
86
	 * @var Column\Action[]
87
	 */
88
	protected $actions = [];
89
90
	/**
91
	 * @var GroupAction\GroupActionCollection
92
	 */
93
	protected $group_action_collection;
94
95
	/**
96
	 * @var Filter\Filter[]
97
	 */
98
	protected $filters = [];
99
100
	/**
101
	 * @var Export\Export[]
102
	 */
103
	protected $exports = [];
104
105
	/**
106
	 * @var DataModel
107
	 */
108
	protected $dataModel;
109
110
	/**
111
	 * @var DataFilter
112
	 */
113
	protected $dataFilter;
114
115
	/**
116
	 * @var string
117
	 */
118
	protected $primary_key = 'id';
119
120
	/**
121
	 * @var bool
122
	 */
123
	protected $do_paginate = TRUE;
124
125
	/**
126
	 * @var bool
127
	 */
128
	protected $csv_export = TRUE;
129
130
	/**
131
	 * @var bool
132
	 */
133
	protected $csv_export_filtered = TRUE;
134
135
	/**
136
	 * @var bool
137
	 */
138
	protected $sortable = FALSE;
139
	
140
	/**
141
	 * @var string
142
	 */
143
	protected $sortable_handler = 'sort!';
144
145
	/**
146
	 * @var string
147
	 */
148
	protected $original_template;
149
150
	/**
151
	 * @var array
152
	 */
153
	protected $redraw_item;
154
155
	/**
156
	 * @var mixed
157
	 */
158
	protected $translator;
159
160
	/**
161
	 * @var bool
162
	 */
163
	protected $force_filter_active;
164
165
	/**
166
	 * @var callable
167
	 */
168
	protected $tree_view_children_callback;
169
170
	/**
171
	 * @var string
172
	 */
173
	protected $tree_view_has_children_column;
174
175
	/**
176
	 * @var bool
177
	 */
178
	protected $outer_filter_rendering = FALSE;
179
180
	/**
181
	 * @var array
182
	 */
183
	protected $columns_export_order = [];
184
185
	/**
186
	 * @var bool
187
	 */
188
	private $remember_state = TRUE;
189
190
	/**
191
	 * @var bool
192
	 */
193
	private $refresh_url = TRUE;
194
195
	/**
196
	 * @var Nette\Http\SessionSection
197
	 */
198
	private $grid_session;
199
200
	/**
201
	 * @var array
202
	 */
203
	private $items_detail = [];
204
205
	/**
206
	 * @var array
207
	 */
208
	private $row_conditions = [
209
		'group_action' => FALSE,
210
		'action' => []
211
	];
212
213
214
	/**
215
	 * @param Nette\ComponentModel\IContainer|NULL $parent
216
	 * @param string                               $name
217
	 */
218
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
219
	{
220
		parent::__construct($parent, $name);
221
222
		$this->monitor('Nette\Application\UI\Presenter');
223
	}
224
225
226
	/**
227
	 * {inheritDoc}
228
	 * @return void
229
	 */
230
	public function attached($presenter)
231
	{
232
		parent::attached($presenter);
233
234
		if ($presenter instanceof Nette\Application\UI\Presenter) {
235
			/**
236
			 * Get session
237
			 */
238
			$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...
239
240
			/**
241
			 * Try to find previous filters/pagination/sort in session
242
			 */
243
			$this->findSessionFilters();
244
		}
245
	}
246
247
248
	/**
249
	 * Find some unique session key name
250
	 * @return string
251
	 */
252
	public function getSessionSectionName()
253
	{
254
		return $this->getPresenter()->getName().':'.$this->getName();
255
	}
256
257
258
	/**
259
	 * Render template
260
	 * @return void
261
	 */
262
	public function render()
263
	{
264
		/**
265
		 * Check whether datagrid has set some columns, initiated data source, etc
266
		 */
267
		if (!($this->dataModel instanceof DataModel)) {
268
			throw new DataGridException('You have to set a data source first.');
269
		}
270
271
		if (empty($this->columns)) {
272
			throw new DataGridException('You have to add at least one column.');
273
		}
274
275
		$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...
276
277
		/**
278
		 * Invoke some possible events
279
		 */
280
		$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...
281
282
		/**
283
		 * Prepare data for rendering (datagrid may render just one item)
284
		 */
285
		$rows = [];
286
287
		if (!empty($this->redraw_item)) {
288
			$items = $this->dataModel->filterRow($this->redraw_item);
289
		} else {
290
			$items = Nette\Utils\Callback::invokeArgs(
291
				[$this->dataModel, 'filterData'],
292
				[
293
					$this->getPaginator(),
294
					$this->sort,
295
					$this->assableFilters()
296
				]
297
			);
298
		}
299
300
		$callback = $this->rowCallback ?: NULL;
301
302
		foreach ($items as $item) {
303
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
304
305
			if ($callback) {
306
				$callback($item, $row->getControl());
307
			}
308
		}
309
310
		if ($this->isTreeView()) {
311
			$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...
312
		}
313
314
		$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...
315
316
		$this->template->columns = $this->columns;
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...
317
		$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...
318
		$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...
319
		$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...
320
321
		$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...
322
		$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...
323
		$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...
324
		$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...
325
326
		/**
327
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
328
		 */
329
		$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...
330
331
		/**
332
		 * Set template file and render it
333
		 */
334
		$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...
335
	}
336
337
338
	/**
339
	 * @param  callable  $callback
340
	 * @return static
341
	 */
342
	public function setRowCallback(callable $callback)
343
	{
344
		$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...
345
		return $this;
346
	}
347
348
349
	/**
350
	 * Return current paginator class
351
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
352
	 */
353
	public function getPaginator()
354
	{
355
		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...
356
			return $this['paginator'];
357
		}
358
359
		return NULL;
360
	}
361
362
363
	/**
364
	 * @param string $primary_key
365
	 */
366
	public function setPrimaryKey($primary_key)
367
	{
368
		$this->primary_key = $primary_key;
369
370
		return $this;
371
	}
372
373
374
	/**
375
	 * Set Grid data source
376
	 * @param DataSource\IDataSource|array|\DibiFluent $source
377
	 * @return DataGrid
378
	 */
379
	public function setDataSource($source)
380
	{
381
		if ($source instanceof DataSource\IDataSource) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
382
			// $source is ready for interact
383
384
		} else if (is_array($source)) {
385
			$data_source = new DataSource\ArrayDataSource($source);
386
387
		} else if ($source instanceof \DibiFluent) {
388
			$driver = $source->getConnection()->getDriver();
389
390
			if ($driver instanceof \DibiOdbcDriver) {
391
				$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key);
392
393
			} else if ($driver instanceof \DibiMsSqlDriver) {
394
				$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key);
395
396
			} else {
397
				$data_source = new DataSource\DibiFluentDataSource($source, $this->primary_key);
398
			}
399
400
		} else if ($source instanceof Nette\Database\Table\Selection) {
0 ignored issues
show
Bug introduced by
The class Nette\Database\Table\Selection does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
401
			$data_source = new DataSource\NetteDatabaseTableDataSource($source, $this->primary_key);
402
403
		} else if ($source instanceof \Kdyby\Doctrine\QueryBuilder) {
0 ignored issues
show
Bug introduced by
The class Kdyby\Doctrine\QueryBuilder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
404
			$data_source = new DataSource\DoctrineDataSource($source, $this->primary_key);
405
406
		} else {
407
			$data_source_class = $source ? get_class($source) : 'NULL';
408
			throw new DataGridException("DataGrid can not take [$data_source_class] as data source.");
409
		}
410
411
		$this->dataModel = new DataModel($data_source);
0 ignored issues
show
Bug introduced by
The variable $data_source does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
412
413
		return $this;
414
	}
415
416
417
	/**
418
	 * Is filter active?
419
	 * @return boolean
420
	 */
421
	public function isFilterActive()
422
	{
423
		$is_filter = ArraysHelper::testTruthy($this->filter);
424
425
		return ($is_filter) || $this->force_filter_active;
426
	}
427
428
429
	/**
430
	 * Tell that filter is active from whatever reasons
431
	 * return self
432
	 */
433
	public function setFilterActive()
434
	{
435
		$this->force_filter_active = TRUE;
436
437
		return $this;
438
	}
439
440
441
	/**
442
	 * If we want to sent some initial filter
443
	 * @param array $filter
444
	 */
445
	public function setFilter(array $filter)
446
	{
447
		$this->filter = $filter;
448
449
		return $this;
450
	}
451
452
453
	/**
454
	 * Set options of select "items_per_page"
455
	 * @param array $items_per_page_list
456
	 */
457
	public function setItemsPerPageList(array $items_per_page_list)
458
	{
459
		$this->items_per_page_list = $items_per_page_list;
460
461
		return $this;
462
	}
463
464
465
	/**
466
	 * Set custom template file to render
467
	 * @param string $template_file
468
	 */
469
	public function setTemplateFile($template_file)
470
	{
471
		$this->template_file = $template_file;
472
473
		return $this;
474
	}
475
476
477
	/**
478
	 * Get DataGrid template file
479
	 * @return string
480
	 */
481
	public function getTemplateFile()
482
	{
483
		return $this->template_file ?: $this->getOriginalTemplateFile();
484
	}
485
486
487
	/**
488
	 * Get DataGrid original template file
489
	 * @return string
490
	 */
491
	public function getOriginalTemplateFile()
492
	{
493
		return __DIR__.'/templates/datagrid.latte';
494
	}
495
496
497
	/**
498
	 * Order Grid to "be paginated"
499
	 * @param bool $do
500
	 */
501
	public function setPagination($do)
502
	{
503
		$this->do_paginate = (bool) $do;
504
505
		return $this;
506
	}
507
508
509
	/**
510
	 * Tell whether Grid is paginated
511
	 * @return bool
512
	 */
513
	public function isPaginated()
514
	{
515
		return $this->do_paginate;
516
	}
517
518
519
	/**
520
	 * Set grido to be sortable
521
	 * @param bool $sortable
522
	 */
523
	public function setSortable($sortable = TRUE)
524
	{
525
		if ($this->getItemsDetail()) {
526
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
527
		}
528
529
		$this->sortable = (bool) $sortable;
530
531
		return $this;
532
	}
533
534
535
	/**
536
	 * Set sortable handle
537
	 * @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...
538
	 */
539
	public function setSortableHandler($handler = 'sort!')
540
	{
541
		$this->sortable_handler = (string) $handler;
542
543
		return $this;
544
	}
545
546
547
	/**
548
	 * Tell whether DataGrid is sortable
549
	 * @return bool
550
	 */
551
	public function isSortable()
552
	{
553
		return $this->sortable;
554
	}
555
556
	/**
557
	 * Return sortable handle name
558
	 * @return string
559
	 */
560
	public function getSortableHandler()
561
	{
562
		return $this->sortable_handler;
563
	}
564
565
566
	/**
567
	 * Is tree view set?
568
	 * @return boolean
569
	 */
570
	public function isTreeView()
571
	{
572
		return (bool) $this->tree_view_children_callback;
573
	}
574
575
576
	/**
577
	 * Setting tree view
578
	 * @param callable $get_children_callback
579
	 * @param string   $tree_view_has_children_column
580
	 */
581
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
582
	{
583
		if (!is_callable($get_children_callback)) {
584
			throw new DataGridException(
585
				'Parameters to method DataGrid::setTreeView must be of type callable'
586
			);
587
		}
588
589
		$this->tree_view_children_callback = $get_children_callback;
590
		$this->tree_view_has_children_column = $tree_view_has_children_column;
591
592
		/**
593
		 * TUrn off pagination
594
		 */
595
		$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...
596
597
		/**
598
		 * Set tree view template file
599
		 */
600
		if (!$this->template_file) {
601
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
602
		}
603
604
		return $this;
605
	}
606
607
608
	/********************************************************************************
609
	 *                                    Columns                                   *
610
	 ********************************************************************************/
611
612
613
	/**
614
	 * Add text column with no other formating
615
	 * @param  string      $key
616
	 * @param  string      $name
617
	 * @param  string|null $column
618
	 * @return Column\Column
619
	 */
620
	public function addColumnText($key, $name, $column = NULL)
621
	{
622
		$this->addColumnCheck($key);
623
		$column = $column ?: $key;
624
625
		return $this->columns[$key] = new Column\ColumnText($column, $name);
626
	}
627
628
629
	/**
630
	 * Add column with link
631
	 * @param  string      $key
632
	 * @param  string      $name
633
	 * @param  string|null $column
634
	 * @return Column\Column
635
	 */
636
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
637
	{
638
		$this->addColumnCheck($key);
639
		$column = $column ?: $key;
640
		$href = $href ?: $key;
641
642
		if (NULL === $params) {
643
			$params = [$this->primary_key];
644
		}
645
646
		return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params);
647
	}
648
649
650
	/**
651
	 * Add column with possible number formating
652
	 * @param  string      $key
653
	 * @param  string      $name
654
	 * @param  string|null $column
655
	 * @return Column\Column
656
	 */
657
	public function addColumnNumber($key, $name, $column = NULL)
658
	{
659
		$this->addColumnCheck($key);
660
		$column = $column ?: $key;
661
662
		return $this->columns[$key] = new Column\ColumnNumber($column, $name);
663
	}
664
665
666
	/**
667
	 * Add column with date formating
668
	 * @param  string      $key
669
	 * @param  string      $name
670
	 * @param  string|null $column
671
	 * @return Column\Column
672
	 */
673
	public function addColumnDateTime($key, $name, $column = NULL)
674
	{
675
		$this->addColumnCheck($key);
676
		$column = $column ?: $key;
677
678
		return $this->columns[$key] = new Column\ColumnDateTime($column, $name);
679
	}
680
681
682
	/**
683
	 * Return existing column
684
	 * @param  string $key
685
	 * @return Column\Column
686
	 * @throws DataGridException
687
	 */
688
	public function getColumn($key)
689
	{
690
		if (!isset($this->columns[$key])) {
691
			throw new DataGridException("There is no column at key [$key] defined.");
692
		}
693
694
		return $this->columns[$key];
695
	}
696
697
698
	/**
699
	 * Remove column
700
	 * @param string $key
701
	 * @return void
702
	 */
703
	public function removeColumn($key)
704
	{
705
		unset($this->columns[$key]);
706
	}
707
708
709
	/**
710
	 * Check whether given key already exists in $this->columns
711
	 * @param  string $key
712
	 * @throws DataGridException
713
	 */
714
	protected function addColumnCheck($key)
715
	{
716
		if (isset($this->columns[$key])) {
717
			throw new DataGridException("There is already column at key [$key] defined.");
718
		}
719
	}
720
721
722
	/********************************************************************************
723
	 *                                    Actions                                   *
724
	 ********************************************************************************/
725
726
727
	/**
728
	 * Create action
729
	 * @param string     $key
730
	 * @param string     $name
731
	 * @param string     $href
732
	 * @param array|null $params
733
	 */
734
	public function addAction($key, $name = '', $href = NULL, array $params = NULL)
735
	{
736
		$this->addActionCheck($key);
737
		$href = $href ?: $key;
738
739
		if (NULL === $params) {
740
			$params = [$this->primary_key];
741
		}
742
743
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
744
	}
745
746
747
	/**
748
	 * Get existing action
749
	 * @param  string       $key
750
	 * @return Column\Action
751
	 * @throws DataGridException
752
	 */
753
	public function getAction($key)
754
	{
755
		if (!isset($this->actions[$key])) {
756
			throw new DataGridException("There is no action at key [$key] defined.");
757
		}
758
759
		return $this->actions[$key];
760
	}
761
762
763
	/**
764
	 * Remove action
765
	 * @param string $key
766
	 * @return void
767
	 */
768
	public function removeAction($key)
769
	{
770
		unset($this->actions[$key]);
771
	}
772
773
774
	/**
775
	 * Check whether given key already exists in $this->filters
776
	 * @param  string $key
777
	 * @throws DataGridException
778
	 */
779
	protected function addActionCheck($key)
780
	{
781
		if (isset($this->actions[$key])) {
782
			throw new DataGridException("There is already action at key [$key] defined.");
783
		}
784
	}
785
786
787
	/********************************************************************************
788
	 *                                    Filters                                   *
789
	 ********************************************************************************/
790
791
792
	/**
793
	 * Add filter fot text search
794
	 * @param string       $key
795
	 * @param string       $name
796
	 * @param array|string $columns
797
	 * @throws DataGridException
798
	 */
799
	public function addFilterText($key, $name, $columns = NULL)
800
	{
801
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
802
803
		if (!is_array($columns)) {
804
			throw new DataGridException("Filter Text can except only array or string.");
805
		}
806
807
		$this->addFilterCheck($key);
808
809
		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...
810
	}
811
812
813
	/**
814
	 * Add select box filter
815
	 * @param string $key
816
	 * @param string $name
817
	 * @param array  $options
818
	 * @param string $column
819
	 * @throws DataGridException
820
	 */
821 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...
822
	{
823
		$column = $column ?: $key;
824
825
		if (!is_string($column)) {
826
			throw new DataGridException("Filter Select can only filter through one column.");
827
		}
828
829
		$this->addFilterCheck($key);
830
831
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
832
	}
833
834
835
	/**
836
	 * Add datepicker filter
837
	 * @param string $key
838
	 * @param string $name
839
	 * @param string $column
840
	 * @throws DataGridException
841
	 */
842
	public function addFilterDate($key, $name, $column = NULL)
843
	{
844
		$column = $column ?: $key;
845
846
		if (!is_string($column)) {
847
			throw new DataGridException("FilterDate can only filter through one column.");
848
		}
849
850
		$this->addFilterCheck($key);
851
852
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
853
	}
854
855
856
	/**
857
	 * Add range filter (from - to)
858
	 * @param string $key
859
	 * @param string $name
860
	 * @param string $column
861
	 * @throws DataGridException
862
	 */
863 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...
864
	{
865
		$column = $column ?: $key;
866
867
		if (!is_string($column)) {
868
			throw new DataGridException("FilterRange can only filter through one column.");
869
		}
870
871
		$this->addFilterCheck($key);
872
873
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
874
	}
875
876
877
	/**
878
	 * Add datepicker filter (from - to)
879
	 * @param string $key
880
	 * @param string $name
881
	 * @param string $column
882
	 * @throws DataGridException
883
	 */
884 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...
885
	{
886
		$column = $column ?: $key;
887
888
		if (!is_string($column)) {
889
			throw new DataGridException("FilterDateRange can only filter through one column.");
890
		}
891
892
		$this->addFilterCheck($key);
893
894
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
895
	}
896
897
898
	/**
899
	 * Check whether given key already exists in $this->filters
900
	 * @param  string $key
901
	 * @throws DataGridException
902
	 */
903
	protected function addFilterCheck($key)
904
	{
905
		if (isset($this->filters[$key])) {
906
			throw new DataGridException("There is already action at key [$key] defined.");
907
		}
908
	}
909
910
911
	/**
912
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
913
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
914
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
915
	 */
916
	public function assableFilters()
917
	{
918
		foreach ($this->filter as $key => $value) {
919
			if (!isset($this->filters[$key])) {
920
				$this->deleteSesssionData($key);
921
922
				continue;
923
			}
924
925
			if (is_array($value) || $value instanceof \Traversable) {
926
				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...
927
					$this->filters[$key]->setValue($value);
928
				}
929
			} else {
930
				if ($value !== '' && $value !== NULL) {
931
					$this->filters[$key]->setValue($value);
932
				}
933
			}
934
		}
935
936
		foreach ($this->columns as $column) {
937
			if (isset($this->sort[$column->getColumnName()])) {
938
				$column->setSort($this->sort);
939
			}
940
		}
941
942
		return $this->filters;
943
	}
944
945
946
	/**
947
	 * Try to restore session stuff
948
	 * @return void
949
	 */
950
	public function findSessionFilters()
951
	{
952
		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...
953
			return;
954
		}
955
956
		if (!$this->remember_state) {
957
			return;
958
		}
959
960
		if ($page = $this->getSessionData('_grid_page')) {
961
			$this->page = $page;
962
		}
963
964
		if ($per_page = $this->getSessionData('_grid_per_page')) {
965
			$this->per_page = $per_page;
966
		}
967
968
		if ($sort = $this->getSessionData('_grid_sort')) {
969
			$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...
970
		}
971
972 View Code Duplication
		foreach ($this->getSessionData() as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
973
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
974
				$this->filter[$key] = $value;
975
			}
976
		}
977
	}
978
979
980
	/**
981
	 * Remove filter
982
	 * @param string $key
983
	 * @return void
984
	 */
985
	public function removeFilter($key)
986
	{
987
		unset($this->filters[$key]);
988
	}
989
990
991
	/********************************************************************************
992
	 *                                    Exports                                   *
993
	 ********************************************************************************/
994
995
996
	/**
997
	 * Add export of type callback
998
	 * @param string   $text
999
	 * @param callable $callback
1000
	 * @param boolean  $filtered
1001
	 */
1002
	public function addExportCallback($text, $callback, $filtered = FALSE)
1003
	{
1004
		if (!is_callable($callback)) {
1005
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1006
		}
1007
1008
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1009
	}
1010
1011
1012
	/**
1013
	 * Add already implemented csv export
1014
	 * @param string $text
1015
	 * @param string $csv_file_name
1016
	 */
1017
	public function addExportCsv($text, $csv_file_name)
1018
	{
1019
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1020
	}
1021
1022
1023
	/**
1024
	 * Add already implemented csv export, but for filtered data
1025
	 * @param string $text
1026
	 * @param string $csv_file_name
1027
	 */
1028
	public function addExportCsvFiltered($text, $csv_file_name)
1029
	{
1030
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1031
	}
1032
1033
1034
	/**
1035
	 * Add export to array
1036
	 * @param Export\Export $export
1037
	 */
1038
	protected function addToExports(Export\Export $export)
1039
	{
1040
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1041
1042
		$export->setLink($this->link('export!', ['id' => $id]));
1043
1044
		return $this->exports[$id] = $export;
1045
	}
1046
1047
1048
	/********************************************************************************
1049
	 *                                 Group actions                                *
1050
	 ********************************************************************************/
1051
1052
1053
	/**
1054
	 * Add group actino
1055
	 * @param string $title
1056
	 * @param array  $options
1057
	 */
1058
	public function addGroupAction($title, $options = [])
1059
	{
1060
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1061
	}
1062
1063
1064
	/**
1065
	 * Get collection of all group actions
1066
	 * @return GroupAction\GroupActionCollection
1067
	 */
1068
	public function getGroupActionCollection()
1069
	{
1070
		if (!$this->group_action_collection) {
1071
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1072
		}
1073
1074
		return $this->group_action_collection;
1075
	}
1076
1077
1078
	/********************************************************************************
1079
	 *                                    Signals                                   *
1080
	 ********************************************************************************/
1081
1082
1083
	/**
1084
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1085
	 * @param  int  $page
1086
	 * @return void
1087
	 */
1088
	public function handlePage($page)
1089
	{
1090
		/**
1091
		 * Session stuff
1092
		 */
1093
		$this->page = $page;
1094
		$this->saveSessionData('_grid_page', $page);
1095
1096
		$this->reload(['table']);
1097
	}
1098
1099
1100
	/**
1101
	 * Handler for sorting
1102
	 * @return void
1103
	 */
1104
	public function handleSort(array $sort)
1105
	{
1106
		/**
1107
		 * Session stuff
1108
		 */
1109
		$this->sort = $sort;
1110
		$this->saveSessionData('_grid_sort', $this->sort);
1111
1112
		$this->reload(['table']);
1113
	}
1114
1115
1116
	/**
1117
	 * handler for reseting the filter
1118
	 * @return void
1119
	 */
1120
	public function handleResetFilter()
1121
	{
1122
		/**
1123
		 * Session stuff
1124
		 */
1125
		$this->deleteSesssionData('_grid_page');
1126
1127 View Code Duplication
		foreach ($this->getSessionData() as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1128
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1129
				$this->deleteSesssionData($key);
1130
			}
1131
		}
1132
1133
		$this->filter = [];
1134
1135
		$this->reload(['grid']);
1136
	}
1137
1138
1139
	/**
1140
	 * Handler for export
1141
	 * @param  int $id Key for particular export class in array $this->exports
1142
	 * @return void
1143
	 */
1144
	public function handleExport($id)
1145
	{
1146
		if (!isset($this->exports[$id])) {
1147
			throw new Nette\Application\ForbiddenRequestException;
1148
		}
1149
1150
		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...
1151
			$this->setColumnsOrder($this->columns_export_order);
1152
		}
1153
1154
		$export = $this->exports[$id];
1155
1156
		if ($export->isFiltered()) {
1157
			$sort      = $this->sort;
1158
			$filter    = $this->assableFilters();
1159
		} else {
1160
			$sort      = $this->primary_key;
1161
			$filter    = [];
1162
		}
1163
1164
		if (NULL === $this->dataModel) {
1165
			throw new DataGridException('You have to set a data source first.');
1166
		}
1167
1168
		$rows = [];
1169
1170
		$items = Nette\Utils\Callback::invokeArgs(
1171
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1172
		);
1173
1174
		foreach ($items as $item) {
1175
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1176
		}
1177
1178
		if ($export instanceof Export\ExportCsv) {
1179
			$export->invoke($rows, $this);
1180
		} else {
1181
			$export->invoke($items, $this);
1182
		}
1183
1184
		if ($export->isAjax()) {
1185
			$this->reload();
1186
		}
1187
	}
1188
1189
1190
	/**
1191
	 * Handler for getting children of parent item (e.g. category)
1192
	 * @param  int $parent
1193
	 * @return void
1194
	 */
1195 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...
1196
	{
1197
		$this->setDataSource(
1198
			call_user_func($this->tree_view_children_callback, $parent)
1199
		);
1200
1201
		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...
1202
			$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...
1203
			$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...
1204
1205
			$this->redrawControl('items');
1206
1207
			$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...
1208
		} else {
1209
			$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...
1210
		}
1211
	}
1212
1213
1214
	/**
1215
	 * Handler for getting item detail
1216
	 * @param  mixed $id
1217
	 * @return void
1218
	 */
1219 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...
1220
	{
1221
		$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...
1222
		$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...
1223
1224
		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...
1225
			$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...
1226
			$this->redrawControl('items');
1227
1228
			$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...
1229
		} else {
1230
			$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...
1231
		}
1232
	}
1233
1234
1235
	/**
1236
	 * Handler for inline editing
1237
	 * @param  mixed $id
1238
	 * @param  mixed $key
1239
	 * @return void
1240
	 */
1241
	public function handleEdit($id, $key)
1242
	{
1243
		$column = $this->getColumn($key);
1244
		$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...
1245
1246
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1247
	}
1248
1249
1250
	/**
1251
	 * Redraw $this
1252
	 * @return void
1253
	 */
1254
	public function reload($snippets = [])
1255
	{
1256
		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...
1257
			$this->redrawControl('tbody');
1258
			$this->redrawControl('pagination');
1259
1260
			/**
1261
			 * manualy reset exports links...
1262
			 */
1263
			$this->resetExportsLinks();
1264
			$this->redrawControl('exports');
1265
1266
			foreach ($snippets as $snippet) {
1267
				$this->redrawControl($snippet);
1268
			}
1269
1270
			$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...
1271
1272
			$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...
1273
		} else {
1274
			$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...
1275
		}
1276
	}
1277
1278
1279
	/**
1280
	 * Redraw just one row via ajax
1281
	 * @param  int   $id
1282
	 * @param  mixed $primary_where_column
1283
	 * @return void
1284
	 */
1285
	public function redrawItem($id, $primary_where_column = NULL)
1286
	{
1287
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1288
1289
		$this->redrawControl('items');
1290
		$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...
1291
1292
		$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...
1293
	}
1294
1295
1296
	/********************************************************************************
1297
	 *                                  Components                                  *
1298
	 ********************************************************************************/
1299
1300
1301
	/**
1302
	 * Paginator factory
1303
	 * @return Components\DataGridPaginator\DataGridPaginator
1304
	 */
1305
	public function createComponentPaginator()
1306
	{
1307
		/**
1308
		 * Init paginator
1309
		 */
1310
		$component = new Components\DataGridPaginator\DataGridPaginator;
1311
		$paginator = $component->getPaginator();
1312
1313
		$paginator->setPage($this->page);
1314
		$paginator->setItemsPerPage($this->getPerPage());
1315
1316
		return $component;
1317
	}
1318
1319
1320
	/**
1321
	 * PerPage form factory
1322
	 * @return Form
1323
	 */
1324
	public function createComponentPerPage()
1325
	{
1326
		$form = new Form;
1327
1328
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1329
			->setValue($this->getPerPage());
1330
1331
		$form->addSubmit('submit', '');
1332
1333
		$saveSessionData = [$this, 'saveSessionData'];
1334
1335
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1336
			/**
1337
			 * Session stuff
1338
			 */
1339
			$saveSessionData('_grid_per_page', $values->per_page);
1340
1341
			/**
1342
			 * Other stuff
1343
			 */
1344
			$this->per_page = $values->per_page;
1345
			$this->reload();
1346
		};
1347
1348
		return $form;
1349
	}
1350
1351
1352
	/**
1353
	 * FilterAndGroupAction form factory
1354
	 * @return Form
1355
	 */
1356
	public function createComponentFilter()
1357
	{
1358
		$form = new Form($this, 'filter');
1359
1360
		$form->setMethod('get');
1361
1362
		/**
1363
		 * Filter part
1364
		 */
1365
		$filter_container = $form->addContainer('filter');
1366
1367
		foreach ($this->filters as $filter) {
1368
			$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...
1369
		}
1370
1371
		/**
1372
		 * Group action part
1373
		 */
1374
		$group_action_container = $form->addContainer('group_action');
1375
1376
		if ($this->hasGroupActions()) {
1377
			$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator());
1378
		}
1379
1380
		$form->setDefaults(['filter' => $this->filter]);
1381
1382
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1383
1384
		return $form;
1385
	}
1386
1387
1388
	/**
1389
	 * Set $this->filter values after filter form submitted
1390
	 * @param  Form $form
1391
	 * @return void
1392
	 */
1393
	public function filterSucceeded(Form $form)
1394
	{
1395
		$values = $form->getValues();
1396
1397
		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...
1398
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1399
				return;
1400
			}
1401
		}
1402
1403
		$values = $values['filter'];
1404
1405
		foreach ($values as $key => $value) {
1406
			/**
1407
			 * Session stuff
1408
			 */
1409
			$this->saveSessionData($key, $value);
1410
1411
			/**
1412
			 * Other stuff
1413
			 */
1414
			$this->filter[$key] = $value;
1415
		}
1416
1417
		$this->reload();
1418
	}
1419
1420
1421
	/********************************************************************************
1422
	 *                               Support functions                              *
1423
	 ********************************************************************************/
1424
1425
1426
	public function resetExportsLinks()
1427
	{
1428
		foreach ($this->exports as $id => $export) {
1429
			$export->setLink($this->link('export!', ['id' => $id]));
1430
		}
1431
	}
1432
1433
1434
	/**
1435
	 * Get parameter per_page
1436
	 * @return int
1437
	 */
1438
	public function getPerPage()
1439
	{
1440
		$per_page = $this->per_page ?: reset($this->items_per_page_list);
1441
1442
		if ($per_page !== 'all' && !in_array($this->per_page, $this->items_per_page_list)) {
1443
			$per_page = reset($this->items_per_page_list);
1444
		}
1445
1446
		return $per_page;
1447
	}
1448
1449
1450
	/**
1451
	 * Get associative array of items_per_page_list
1452
	 * @return array
1453
	 */
1454
	public function getItemsPerPageList()
1455
	{
1456
		$list = array_flip($this->items_per_page_list);
1457
1458
		foreach ($list as $key => $value) {
1459
			$list[$key] = $key;
1460
		}
1461
1462
		$list['all'] = $this->getTranslator()->translate('All');
1463
1464
		return $list;
1465
	}
1466
1467
1468
	/**
1469
	 * Get primary key of datagrid data source
1470
	 * @return string
1471
	 */
1472
	public function getPrimaryKey()
1473
	{
1474
		return $this->primary_key;
1475
	}
1476
1477
1478
	/**
1479
	 * Get set of set columns
1480
	 * @return Column\IColumn[]
1481
	 */
1482
	public function getColumns()
1483
	{
1484
		return $this->columns;
1485
	}
1486
1487
1488
	/**
1489
	 * Has datagrid some group actions?
1490
	 * @return boolean
1491
	 */
1492
	public function hasGroupActions()
1493
	{
1494
		return (bool) $this->group_action_collection;
1495
	}
1496
1497
1498
	/**
1499
	 * Get translator for datagrid
1500
	 * @return Nette\Localization\ITranslator
1501
	 */
1502
	public function getTranslator()
1503
	{
1504
		if (!$this->translator) {
1505
			$this->translator = new Localization\SimpleTranslator;
1506
		}
1507
1508
		return $this->translator;
1509
	}
1510
1511
1512
	/**
1513
	 * Set datagrid translator
1514
	 * @param Nette\Localization\ITranslator $translator
1515
	 */
1516
	public function setTranslator(Nette\Localization\ITranslator $translator)
1517
	{
1518
		$this->translator = $translator;
1519
1520
		return $this;
1521
	}
1522
1523
1524
	/**
1525
	 * Should be datagrid filters rendered separately?
1526
	 * @param boolean $out
1527
	 */
1528
	public function setOuterFilterRendering($out = TRUE)
1529
	{
1530
		$this->outer_filter_rendering = (bool) $out;
1531
1532
		return $this;
1533
	}
1534
1535
1536
	/**
1537
	 * Are datagrid filters rendered separately?
1538
	 * @return boolean
1539
	 */
1540
	public function hasOuterFilterRendering()
1541
	{
1542
		return $this->outer_filter_rendering;
1543
	}
1544
1545
1546
	/**
1547
	 * Set order of datagrid columns
1548
	 * @param array $order
1549
	 */
1550
	public function setColumnsOrder($order)
1551
	{
1552
		$new_order = [];
1553
1554
		foreach ($order as $key) {
1555
			if (isset($this->columns[$key])) {
1556
				$new_order[$key] = $this->columns[$key];
1557
			}
1558
		}
1559
1560
		if (sizeof($new_order) === sizeof($this->columns)) {
1561
			$this->columns = $new_order;
1562
		} else {
1563
			throw new DataGridException('When changing columns order, you have to specify all columns');
1564
		}
1565
1566
		return $this;
1567
	}
1568
1569
1570
	/**
1571
	 * Columns order may be different for export and normal grid
1572
	 * @param array $order
1573
	 */
1574
	public function setColumnsExportOrder($order)
1575
	{
1576
		$this->columns_export_order = (array) $order;
1577
	}
1578
1579
1580
	/**
1581
	 * Should datagrid remember its filters/pagination/etc using session?
1582
	 * @param bool $remember
1583
	 */
1584
	public function setRememberState($remember = TRUE)
1585
	{
1586
		$this->remember_state = (bool) $remember;
1587
1588
		return $this;
1589
	}
1590
1591
1592
	/**
1593
	 * Should datagrid refresh url using history API?
1594
	 * @param bool $refresh
1595
	 */
1596
	public function setRefreshUrl($refresh = TRUE)
1597
	{
1598
		$this->refresh_url = (bool) $refresh;
1599
1600
1601
		return $this;
1602
	}
1603
1604
1605
	/**
1606
	 * Get session data if functionality is enabled
1607
	 * @param  string $key
1608
	 * @return mixed
1609
	 */
1610
	public function getSessionData($key = NULL)
1611
	{
1612
		if (!$this->remember_state) {
1613
			return $key ? NULL : [];
1614
		}
1615
1616
		return $key ? $this->grid_session->{$key} : $this->grid_session;
1617
	}
1618
1619
1620
	/**
1621
	 * Save session data - just if it is enabled
1622
	 * @param  string $key
1623
	 * @param  mixed  $value
1624
	 * @return void
1625
	 */
1626
	public function saveSessionData($key, $value)
1627
	{
1628
1629
		if ($this->remember_state) {
1630
			$this->grid_session->{$key} = $value;
1631
		}
1632
	}
1633
1634
1635
	/**
1636
	 * Delete session data
1637
	 * @return void
1638
	 */
1639
	public function deleteSesssionData($key)
1640
	{
1641
		unset($this->grid_session->{$key});
1642
	}
1643
1644
1645
	/**
1646
	 * Get items detail parameters
1647
	 * @return array
1648
	 */
1649
	public function getItemsDetail()
1650
	{
1651
		return $this->items_detail;
1652
	}
1653
1654
1655
	/**
1656
	 * Items can have thair detail - toggled
1657
	 * @param mixed $detail callable|string|bool
1658
	 */
1659
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1660
	{
1661
		if ($this->isSortable()) {
1662
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1663
		}
1664
1665
		$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...
1666
			$this,
1667
			$primary_where_column ?: $this->primary_key
1668
		);
1669
1670
		if (is_string($detail)) {
1671
			/**
1672
			 * Item detail will be in separate template
1673
			 */
1674
			$this->items_detail->setType('template');
1675
			$this->items_detail->setTemplate($detail);
1676
1677
		} else if (is_callable($detail)) {
1678
			/**
1679
			 * Item detail will be rendered via custom callback renderer
1680
			 */
1681
			$this->items_detail->setType('renderer');
1682
			$this->items_detail->setRenderer($detail);
1683
1684
		} else if (TRUE === $detail) {
1685
			/**
1686
			 * Item detail will be rendered probably via block #detail
1687
			 */
1688
			$this->items_detail->setType('block');
1689
1690
		} else {
1691
			throw new DataGridException(
1692
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1693
			);
1694
		}
1695
1696
		return $this->items_detail;
1697
	}
1698
1699
1700
	/**
1701
	 * Get cont of columns
1702
	 * @return int
1703
	 */
1704
	public function getColumnsCount()
1705
	{
1706
		$count = sizeof($this->columns);
1707
1708
		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...
1709
			$count++;
1710
		}
1711
1712
		if ($this->hasGroupActions()) {
1713
			$count++;
1714
		}
1715
1716
		return $count;
1717
	}
1718
1719
1720
	public function allowRowsGroupAction(callable $condition)
1721
	{
1722
		$this->row_conditions['group_action'] = $condition;
1723
	}
1724
1725
1726
	public function allowRowsAction($key, callable $condition)
1727
	{
1728
		$this->row_conditions['action'][$key] = $condition;
1729
	}
1730
1731
1732
	public function getRowCondition($name, $key = NULL)
1733
	{
1734
		if (!isset($this->row_conditions[$name])) {
1735
			return FALSE;
1736
		}
1737
1738
		$condition = $this->row_conditions[$name];
1739
1740
		if (!$key) {
1741
			return $condition;
1742
		}
1743
1744
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1745
	}
1746
1747
}
1748
1749
1750
class DataGridException extends \Exception
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1751
{
1752
}
1753