Completed
Push — master ( a21e1b...e6e660 )
by Pavel
02:28
created

DataGrid::allowRowsAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2015 ublaboo <[email protected]>
5
 * @author      Pavel Janda <[email protected]>
6
 * @package     Ublaboo
7
 */
8
9
namespace Ublaboo\DataGrid;
10
11
use Nette;
12
use Ublaboo\DataGrid\Utils\ArraysHelper;
13
use Nette\Application\UI\Form;
14
15
class DataGrid extends Nette\Application\UI\Control
16
{
17
18
	/**
19
	 * @var string
20
	 * @todo Tell about this on github
21
	 */
22
	public static $icon_prefix = 'fa fa-';
23
24
	/**
25
	 * @var int
26
	 * @persistent
27
	 */
28
	public $page = 1;
29
30
	/**
31
	 * @var int
32
	 * @persistent
33
	 */
34
	public $per_page;
35
36
	/**
37
	 * @var array
38
	 * @persistent
39
	 */
40
	public $sort = [];
41
42
	/**
43
	 * @var array
44
	 * @persistent
45
	 */
46
	public $filter = [];
47
48
	/**
49
	 * @var Callable[]
50
	 */
51
	public $onRender = [];
52
53
	/**
54
	 * @var array
55
	 */
56
	protected $items_per_page_list = [10, 20, 50];
57
58
	/**
59
	 * @var string
60
	 */
61
	protected $template_file;
62
63
	/**
64
	 * @var Column\IColumn[]
65
	 */
66
	protected $columns = [];
67
68
	/**
69
	 * @var Column\Action[]
70
	 */
71
	protected $actions = [];
72
73
	/**
74
	 * @var GroupAction\GroupActionCollection
75
	 */
76
	protected $group_action_collection;
77
78
	/**
79
	 * @var Filter\Filter[]
80
	 */
81
	protected $filters = [];
82
83
	/**
84
	 * @var Export\Export[]
85
	 */
86
	protected $exports = [];
87
88
	/**
89
	 * @var DataModel
90
	 */
91
	protected $dataModel;
92
93
	/**
94
	 * @var DataFilter
95
	 */
96
	protected $dataFilter;
97
98
	/**
99
	 * @var string
100
	 */
101
	protected $primary_key = 'id';
102
103
	/**
104
	 * @var bool
105
	 */
106
	protected $do_paginate = TRUE;
107
108
	/**
109
	 * @var bool
110
	 */
111
	protected $csv_export = TRUE;
112
113
	/**
114
	 * @var bool
115
	 */
116
	protected $csv_export_filtered = TRUE;
117
118
	/**
119
	 * @var bool
120
	 */
121
	protected $sortable = FALSE;
122
123
	/**
124
	 * @var string
125
	 */
126
	protected $original_template;
127
128
	/**
129
	 * @var array
130
	 */
131
	protected $redraw_item;
132
133
	/**
134
	 * @var mixed
135
	 */
136
	protected $translator;
137
138
	/**
139
	 * @var bool
140
	 */
141
	protected $force_filter_active;
142
143
	/**
144
	 * @var callable
145
	 */
146
	protected $tree_view_children_callback;
147
148
	/**
149
	 * @var string
150
	 */
151
	protected $tree_view_has_children_column;
152
153
	/**
154
	 * @var bool
155
	 */
156
	protected $outer_filter_rendering = FALSE;
157
158
	/**
159
	 * @var bool
160
	 */
161
	private $remember_state = TRUE;
162
163
	/**
164
	 * @var bool
165
	 */
166
	private $refresh_url = TRUE;
167
168
	/**
169
	 * @var Nette\Http\SessionSection
170
	 */
171
	private $grid_session;
172
173
	/**
174
	 * @var array
175
	 */
176
	private $items_detail = [];
177
178
	/**
179
	 * @var array
180
	 */
181
	private $row_conditions = [
182
		'group_action' => FALSE,
183
		'action' => []
184
	];
185
186
187
	/**
188
	 * @param Nette\ComponentModel\IContainer|NULL $parent
189
	 * @param string                               $name
190
	 */
191
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
192
	{
193
		parent::__construct($parent, $name);
194
195
		$this->monitor('Nette\Application\UI\Presenter');
196
	}
197
198
199
	/**
200
	 * {inheritDoc}
201
	 * @return void
202
	 */
203
	public function attached($presenter)
204
	{
205
		parent::attached($presenter);
206
207
		if ($presenter instanceof Nette\Application\UI\Presenter) {
208
			/**
209
			 * Get session
210
			 */
211
			$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...
212
213
			/**
214
			 * Try to find previous filters/pagination/sort in session
215
			 */
216
			$this->findSessionFilters();
217
		}
218
	}
219
220
221
	/**
222
	 * Find some unique session key name
223
	 * @return string
224
	 */
225
	public function getSessionSectionName()
226
	{
227
		return $this->getPresenter()->getName().':'.$this->getName();
228
	}
229
230
231
	/**
232
	 * Render template
233
	 * @return void
234
	 */
235
	public function render()
236
	{
237
		$rows = [];
238
239
		/**
240
		 * Check whether datagrid has set some columns, initiated data source, etc
241
		 */
242
		if (!($this->dataModel instanceof DataModel)) {
243
			throw new DataGridException('You have to set a data source first.');
244
		}
245
246
		if (empty($this->columns)) {
247
			throw new DataGridException('You have to add at least one column.');
248
		}
249
250
		$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...
251
252
		/**
253
		 * Invoke some possible events
254
		 */
255
		$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...
256
257
		/**
258
		 * Prepare data for rendering (datagrid may render just one item)
259
		 */
260
		if (!empty($this->redraw_item)) {
261
			$items = $this->dataModel->filterRow($this->redraw_item);
262
		} else {
263
			$items = Nette\Utils\Callback::invokeArgs(
264
				[$this->dataModel, 'filterData'],
265
				[
266
					$this->getPaginator(),
267
					$this->sort,
268
					$this->assableFilters()
269
				]
270
			);
271
		}
272
273
		foreach ($items as $item) {
274
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
275
		}
276
277
		if ($this->isTreeView()) {
278
			$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...
279
		}
280
281
		$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...
282
283
		$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...
284
		$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...
285
		$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...
286
		$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...
287
288
		$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...
289
		$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...
290
		$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...
291
		$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...
292
293
		/**
294
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
295
		 */
296
		$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...
297
298
		/**
299
		 * Set template file and render it
300
		 */
301
		$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...
302
	}
303
304
305
	/**
306
	 * Return current paginator class
307
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
308
	 */
309
	public function getPaginator()
310
	{
311
		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...
312
			return $this['paginator'];
313
		}
314
315
		return NULL;
316
	}
317
318
319
	/**
320
	 * @param string $primary_key
321
	 */
322
	public function setPrimaryKey($primary_key)
323
	{
324
		$this->primary_key = $primary_key;
325
326
		return $this;
327
	}
328
329
330
	/**
331
	 * Set Grid data source
332
	 * @param DataSource\IDataSource|array|\DibiFluent $source
333
	 * @return DataGrid
334
	 */
335
	public function setDataSource($source)
336
	{
337
		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...
338
			// $source is ready for interact
339
340
		} else if (is_array($source)) {
341
			$data_source = new DataSource\ArrayDataSource($source);
342
343
		} else if ($source instanceof \DibiFluent) {
0 ignored issues
show
Bug introduced by
The class DibiFluent 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...
344
			$driver = $source->getConnection()->getDriver();
345
346
			if ($driver instanceof \DibiOdbcDriver) {
0 ignored issues
show
Bug introduced by
The class DibiOdbcDriver 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...
347
				$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key);
348
349
			} else if ($driver instanceof \DibiMsSqlDriver) {
0 ignored issues
show
Bug introduced by
The class DibiMsSqlDriver 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...
350
				$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key);
351
352
			} else {
353
				$data_source = new DataSource\DibiFluentDataSource($source, $this->primary_key);
354
			}
355
356
		} 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...
357
			$data_source = new DataSource\NetteDatabaseTableDataSource($source, $this->primary_key);
358
359
		} 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...
360
			$data_source = new DataSource\DoctrineDataSource($source, $this->primary_key);
361
362
		} else {
363
			$data_source_class = $source ? get_class($source) : 'NULL';
364
			throw new DataGridException("DataGrid can not take [$data_source_class] as data source.");
365
		}
366
367
		$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...
Bug introduced by
It seems like $data_source defined by new \Ublaboo\DataGrid\Da...rrayDataSource($source) on line 341 can also be of type object<Ublaboo\DataGrid\...Source\ArrayDataSource>; however, Ublaboo\DataGrid\DataModel::__construct() does only seem to accept object<Ublaboo\DataGrid\DataSource\IDataSource>, 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...
368
369
		return $this;
370
	}
371
372
373
	/**
374
	 * Is filter active?
375
	 * @return boolean
376
	 */
377
	public function isFilterActive()
378
	{
379
		$is_filter = ArraysHelper::testTruthy($this->filter);
380
381
		return ($is_filter) || $this->force_filter_active;
382
	}
383
384
385
	/**
386
	 * Tell that filter is active from whatever reasons
387
	 * return self
388
	 */
389
	public function setFilterActive()
390
	{
391
		$this->force_filter_active = TRUE;
392
393
		return $this;
394
	}
395
396
397
	/**
398
	 * If we want to sent some initial filter
399
	 * @param array $filter
400
	 */
401
	public function setFilter(array $filter)
402
	{
403
		$this->filter = $filter;
404
405
		return $this;
406
	}
407
408
409
	/**
410
	 * Set options of select "items_per_page"
411
	 * @param array $items_per_page_list
412
	 */
413
	public function setItemsPerPageList(array $items_per_page_list)
414
	{
415
		$this->items_per_page_list = $items_per_page_list;
416
417
		return $this;
418
	}
419
420
421
	/**
422
	 * Set custom template file to render
423
	 * @param string $template_file
424
	 */
425
	public function setTemplateFile($template_file)
426
	{
427
		$this->template_file = $template_file;
428
429
		return $this;
430
	}
431
432
433
	/**
434
	 * Get DataGrid template file
435
	 * @return string
436
	 */
437
	public function getTemplateFile()
438
	{
439
		return $this->template_file ?: $this->getOriginalTemplateFile();
440
	}
441
442
443
	/**
444
	 * Get DataGrid original template file
445
	 * @return string
446
	 */
447
	public function getOriginalTemplateFile()
448
	{
449
		return __DIR__.'/templates/datagrid.latte';
450
	}
451
452
453
	/**
454
	 * Order Grid to "be paginated"
455
	 * @param bool $do
456
	 */
457
	public function setPagination($do)
458
	{
459
		$this->do_paginate = (bool) $do;
460
461
		return $this;
462
	}
463
464
465
	/**
466
	 * Tell whether Grid is paginated
467
	 * @return bool
468
	 */
469
	public function isPaginated()
470
	{
471
		return $this->do_paginate;
472
	}
473
474
475
	/**
476
	 * Set grido to be sortable
477
	 * @param bool $sortable
478
	 */
479
	public function setSortable($sortable = TRUE)
480
	{
481
		if ($this->getItemsDetail()) {
482
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
483
		}
484
485
		$this->sortable = (bool) $sortable;
486
487
		return $this;
488
	}
489
490
491
	/**
492
	 * Tell whether DataGrid is sortable
493
	 * @return bool
494
	 */
495
	public function isSortable()
496
	{
497
		return $this->sortable;
498
	}
499
500
501
	/**
502
	 * Is tree view set?
503
	 * @return boolean
504
	 */
505
	public function isTreeView()
506
	{
507
		return (bool) $this->tree_view_children_callback;
508
	}
509
510
511
	/**
512
	 * Setting tree view
513
	 * @param callable $get_children_callback
514
	 * @param string   $tree_view_has_children_column
515
	 */
516
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
517
	{
518
		if (!is_callable($get_children_callback)) {
519
			throw new DataGridException(
520
				'Parameters to method DataGrid::setTreeView must be of type callable'
521
			);
522
		}
523
524
		$this->tree_view_children_callback = $get_children_callback;
525
		$this->tree_view_has_children_column = $tree_view_has_children_column;
526
527
		/**
528
		 * TUrn off pagination
529
		 */
530
		$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...
531
532
		/**
533
		 * Set tree view template file
534
		 */
535
		if (!$this->template_file) {
536
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
537
		}
538
539
		return $this;
540
	}
541
542
543
	/********************************************************************************
544
	 *                                    Columns                                   *
545
	 ********************************************************************************/
546
547
548
	/**
549
	 * Add text column with no other formating
550
	 * @param  string      $key
551
	 * @param  string      $name
552
	 * @param  string|null $column
553
	 * @return Column\Column
554
	 */
555
	public function addColumnText($key, $name, $column = NULL)
556
	{
557
		$this->addColumnCheck($key);
558
		$column = $column ?: $key;
559
560
		return $this->columns[$key] = new Column\ColumnText($column, $name);
561
	}
562
563
564
	/**
565
	 * Add column with link
566
	 * @param  string      $key
567
	 * @param  string      $name
568
	 * @param  string|null $column
569
	 * @return Column\Column
570
	 */
571
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
572
	{
573
		$this->addColumnCheck($key);
574
		$column = $column ?: $key;
575
		$href = $href ?: $key;
576
577
		if (NULL === $params) {
578
			$params = [$this->primary_key];
579
		}
580
581
		return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params);
582
	}
583
584
585
	/**
586
	 * Add column with possible number formating
587
	 * @param  string      $key
588
	 * @param  string      $name
589
	 * @param  string|null $column
590
	 * @return Column\Column
591
	 */
592
	public function addColumnNumber($key, $name, $column = NULL)
593
	{
594
		$this->addColumnCheck($key);
595
		$column = $column ?: $key;
596
597
		return $this->columns[$key] = new Column\ColumnNumber($column, $name);
598
	}
599
600
601
	/**
602
	 * Add column with date formating
603
	 * @param  string      $key
604
	 * @param  string      $name
605
	 * @param  string|null $column
606
	 * @return Column\Column
607
	 */
608
	public function addColumnDateTime($key, $name, $column = NULL)
609
	{
610
		$this->addColumnCheck($key);
611
		$column = $column ?: $key;
612
613
		return $this->columns[$key] = new Column\ColumnDateTime($column, $name);
614
	}
615
616
617
	/**
618
	 * Return existing column
619
	 * @param  string $key
620
	 * @return Column\Column
621
	 * @throws DataGridException
622
	 */
623
	public function getColumn($key)
624
	{
625
		if (!isset($this->columns[$key])) {
626
			throw new DataGridException("There is no column at key [$key] defined.");
627
		}
628
629
		return $this->columns[$key];
630
	}
631
632
633
	/**
634
	 * Remove column
635
	 * @param string $key
636
	 * @return void
637
	 */
638
	public function removeColumn($key)
639
	{
640
		unset($this->columns[$key]);
641
	}
642
643
644
	/**
645
	 * Check whether given key already exists in $this->columns
646
	 * @param  string $key
647
	 * @throws DataGridException
648
	 */
649
	protected function addColumnCheck($key)
650
	{
651
		if (isset($this->columns[$key])) {
652
			throw new DataGridException("There is already column at key [$key] defined.");
653
		}
654
	}
655
656
657
	/********************************************************************************
658
	 *                                    Actions                                   *
659
	 ********************************************************************************/
660
661
662
	/**
663
	 * Create action
664
	 * @param string     $key
665
	 * @param string     $name
666
	 * @param string     $href
667
	 * @param array|null $params
668
	 */
669
	public function addAction($key, $name = '', $href = NULL, array $params = NULL)
670
	{
671
		$this->addActionCheck($key);
672
		$href = $href ?: $key;
673
674
		if (NULL === $params) {
675
			$params = [$this->primary_key];
676
		}
677
678
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
679
	}
680
681
682
	/**
683
	 * Get existing action
684
	 * @param  string       $key
685
	 * @return Column\Action
686
	 * @throws DataGridException
687
	 */
688
	public function getAction($key)
689
	{
690
		if (!isset($this->actions[$key])) {
691
			throw new DataGridException("There is no action at key [$key] defined.");
692
		}
693
694
		return $this->actions[$key];
695
	}
696
697
698
	/**
699
	 * Remove action
700
	 * @param string $key
701
	 * @return void
702
	 */
703
	public function removeAction($key)
704
	{
705
		unset($this->actions[$key]);
706
	}
707
708
709
	/**
710
	 * Check whether given key already exists in $this->filters
711
	 * @param  string $key
712
	 * @throws DataGridException
713
	 */
714
	protected function addActionCheck($key)
715
	{
716
		if (isset($this->actions[$key])) {
717
			throw new DataGridException("There is already action at key [$key] defined.");
718
		}
719
	}
720
721
722
	/********************************************************************************
723
	 *                                    Filters                                   *
724
	 ********************************************************************************/
725
726
727
	/**
728
	 * Add filter fot text search
729
	 * @param string       $key
730
	 * @param string       $name
731
	 * @param array|string $columns
732
	 * @throws DataGridException
733
	 */
734
	public function addFilterText($key, $name, $columns = NULL)
735
	{
736
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
737
738
		if (!is_array($columns)) {
739
			throw new DataGridException("Filter Text can except only array or string.");
740
		}
741
742
		$this->addFilterCheck($key);
743
744
		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...
745
	}
746
747
748
	/**
749
	 * Add select box filter
750
	 * @param string $key
751
	 * @param string $name
752
	 * @param array  $options
753
	 * @param string $column
754
	 * @throws DataGridException
755
	 */
756 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...
757
	{
758
		$column = $column ?: $key;
759
760
		if (!is_string($column)) {
761
			throw new DataGridException("Filter Select can only filter through one column.");
762
		}
763
764
		$this->addFilterCheck($key);
765
766
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
767
	}
768
769
770
	/**
771
	 * Add datepicker filter
772
	 * @param string $key
773
	 * @param string $name
774
	 * @param string $column
775
	 * @throws DataGridException
776
	 */
777
	public function addFilterDate($key, $name, $column = NULL)
778
	{
779
		$column = $column ?: $key;
780
781
		if (!is_string($column)) {
782
			throw new DataGridException("FilterDate can only filter through one column.");
783
		}
784
785
		$this->addFilterCheck($key);
786
787
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
788
	}
789
790
791
	/**
792
	 * Add range filter (from - to)
793
	 * @param string $key
794
	 * @param string $name
795
	 * @param string $column
796
	 * @throws DataGridException
797
	 */
798 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...
799
	{
800
		$column = $column ?: $key;
801
802
		if (!is_string($column)) {
803
			throw new DataGridException("FilterRange can only filter through one column.");
804
		}
805
806
		$this->addFilterCheck($key);
807
808
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
809
	}
810
811
812
	/**
813
	 * Add datepicker filter (from - to)
814
	 * @param string $key
815
	 * @param string $name
816
	 * @param string $column
817
	 * @throws DataGridException
818
	 */
819 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...
820
	{
821
		$column = $column ?: $key;
822
823
		if (!is_string($column)) {
824
			throw new DataGridException("FilterDateRange can only filter through one column.");
825
		}
826
827
		$this->addFilterCheck($key);
828
829
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
830
	}
831
832
833
	/**
834
	 * Check whether given key already exists in $this->filters
835
	 * @param  string $key
836
	 * @throws DataGridException
837
	 */
838
	protected function addFilterCheck($key)
839
	{
840
		if (isset($this->filters[$key])) {
841
			throw new DataGridException("There is already action at key [$key] defined.");
842
		}
843
	}
844
845
846
	/**
847
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
848
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
849
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
850
	 */
851
	public function assableFilters()
852
	{
853
		foreach ($this->filter as $key => $value) {
854
			if (!isset($this->filters[$key])) {
855
				$this->deleteSesssionData($key);
856
857
				continue;
858
			}
859
860
			if (is_array($value) || $value instanceof \Traversable) {
861
				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...
862
					$this->filters[$key]->setValue($value);
863
				}
864
			} else {
865
				if ($value !== '' && $value !== NULL) {
866
					$this->filters[$key]->setValue($value);
867
				}
868
			}
869
		}
870
871
		foreach ($this->columns as $column) {
872
			if (isset($this->sort[$column->getColumnName()])) {
873
				$column->setSort($this->sort);
874
			}
875
		}
876
877
		return $this->filters;
878
	}
879
880
881
	/**
882
	 * Try to restore session stuff
883
	 * @return void
884
	 */
885
	public function findSessionFilters()
886
	{
887
		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...
888
			return;
889
		}
890
891
		if (!$this->remember_state) {
892
			return;
893
		}
894
895
		if ($page = $this->getSessionData('_grid_page')) {
896
			$this->page = $page;
897
		}
898
899
		if ($per_page = $this->getSessionData('_grid_per_page')) {
900
			$this->per_page = $per_page;
901
		}
902
903
		if ($sort = $this->getSessionData('_grid_sort')) {
904
			$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...
905
		}
906
907
		foreach ($this->getSessionData() as $key => $value) {
908
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
909
				$this->filter[$key] = $value;
910
			}
911
		}
912
	}
913
914
915
	/**
916
	 * Remove filter
917
	 * @param string $key
918
	 * @return void
919
	 */
920
	public function removeFilter($key)
921
	{
922
		unset($this->filters[$key]);
923
	}
924
925
926
	/********************************************************************************
927
	 *                                    Exports                                   *
928
	 ********************************************************************************/
929
930
931
	/**
932
	 * Add export of type callback
933
	 * @param string   $text
934
	 * @param callable $callback
935
	 * @param boolean  $filtered
936
	 */
937
	public function addExportCallback($text, $callback, $filtered = FALSE)
938
	{
939
		if (!is_callable($callback)) {
940
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
941
		}
942
943
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
944
	}
945
946
947
	/**
948
	 * Add already implemented csv export
949
	 * @param string $text
950
	 * @param string $csv_file_name
951
	 */
952
	public function addExportCsv($text, $csv_file_name)
953
	{
954
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
955
	}
956
957
958
	/**
959
	 * Add already implemented csv export, but for filtered data
960
	 * @param string $text
961
	 * @param string $csv_file_name
962
	 */
963
	public function addExportCsvFiltered($text, $csv_file_name)
964
	{
965
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
966
	}
967
968
969
	/**
970
	 * Add export to array
971
	 * @param Export\Export $export
972
	 */
973
	protected function addToExports(Export\Export $export)
974
	{
975
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
976
977
		$export->setLink($this->link('export!', ['id' => $id]));
978
979
		return $this->exports[$id] = $export;
980
	}
981
982
983
	/********************************************************************************
984
	 *                                 Group actions                                *
985
	 ********************************************************************************/
986
987
988
	/**
989
	 * Add group actino
990
	 * @param string $title
991
	 * @param array  $options
992
	 */
993
	public function addGroupAction($title, $options = [])
994
	{
995
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
996
	}
997
998
999
	/**
1000
	 * Get collection of all group actions
1001
	 * @return GroupAction\GroupActionCollection
1002
	 */
1003
	public function getGroupActionCollection()
1004
	{
1005
		if (!$this->group_action_collection) {
1006
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1007
		}
1008
1009
		return $this->group_action_collection;
1010
	}
1011
1012
1013
	/********************************************************************************
1014
	 *                                    Signals                                   *
1015
	 ********************************************************************************/
1016
1017
1018
	/**
1019
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1020
	 * @param  int  $page
1021
	 * @return void
1022
	 */
1023
	public function handlePage($page)
1024
	{
1025
		/**
1026
		 * Session stuff
1027
		 */
1028
		$this->page = $page;
1029
		$this->saveSessionData('_grid_page', $page);
1030
1031
		$this->reload(['table']);
1032
	}
1033
1034
1035
	/**
1036
	 * Handler for sorting
1037
	 * @return void
1038
	 */
1039
	public function handleSort(array $sort)
1040
	{
1041
		/**
1042
		 * Session stuff
1043
		 */
1044
		$this->sort = $sort;
1045
		$this->saveSessionData('_grid_sort', $this->sort);
1046
1047
		$this->reload(['table']);
1048
	}
1049
1050
1051
	/**
1052
	 * handler for reseting the filter
1053
	 * @return void
1054
	 */
1055
	public function handleResetFilter()
1056
	{
1057
		/**
1058
		 * Session stuff
1059
		 */
1060
		$this->deleteSesssionData('_grid_page');
1061
1062
		foreach ($this->getSessionData() as $key => $value) {
1063
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1064
				$this->deleteSesssionData($key);
1065
				if ($value instanceof \Traversable) {
1066
					foreach ($value as $key2 => $value2) {
1067
						$this->filter[$key][$key2] = NULL;
1068
					}
1069
				} else {
1070
					$this->filter[$key] = NULL;
1071
				}
1072
			}
1073
		}
1074
1075
		$this->reload(['grid']);
1076
	}
1077
1078
1079
	/**
1080
	 * Handler for export
1081
	 * @param  int $id Key for particular export class in array $this->exports
1082
	 * @return void
1083
	 */
1084
	public function handleExport($id)
1085
	{
1086
		if (!isset($this->exports[$id])) {
1087
			throw new Nette\Application\ForbiddenRequestException;
1088
		}
1089
1090
		$export = $this->exports[$id];
1091
1092
		if ($export->isFiltered()) {
1093
			$sort      = $this->sort;
1094
			$filter    = $this->assableFilters();
1095
		} else {
1096
			$sort      = $this->primary_key;
1097
			$filter    = [];
1098
		}
1099
1100
		if (NULL === $this->dataModel) {
1101
			throw new DataGridException('You have to set a data source first.');
1102
		}
1103
1104
		$data = Nette\Utils\Callback::invokeArgs(
1105
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1106
		);
1107
1108
		$export->invoke($data, $this);
1109
1110
		if ($export->isAjax()) {
1111
			$this->reload();
1112
		}
1113
	}
1114
1115
1116
	/**
1117
	 * Handler for getting children of parent item (e.g. category)
1118
	 * @param  int $parent
1119
	 * @return void
1120
	 */
1121 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...
1122
	{
1123
		$this->setDataSource(
1124
			call_user_func($this->tree_view_children_callback, $parent)
1125
		);
1126
1127
		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...
1128
			$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...
1129
			$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...
1130
1131
			$this->redrawControl('items');
1132
		} else {
1133
			$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...
1134
		}
1135
	}
1136
1137
1138
	/**
1139
	 * Handler for getting item detail
1140
	 * @param  mixed $id
1141
	 * @return void
1142
	 */
1143 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...
1144
	{
1145
		$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...
1146
		$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...
1147
1148
		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...
1149
			$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...
1150
			$this->redrawControl('items');
1151
		} else {
1152
			$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...
1153
		}
1154
	}
1155
1156
1157
	/**
1158
	 * Handler for inline editing
1159
	 * @param  mixed $id
1160
	 * @param  mixed $key
1161
	 * @return void
1162
	 */
1163
	public function handleEdit($id, $key)
1164
	{
1165
		$column = $this->getColumn($key);
1166
		$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...
1167
1168
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1169
	}
1170
1171
1172
	/**
1173
	 * Redraw $this
1174
	 * @return void
1175
	 */
1176
	public function reload($snippets = [])
1177
	{
1178
		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...
1179
			$this->redrawControl('tbody');
1180
			$this->redrawControl('pagination');
1181
1182
			/**
1183
			 * manualy reset exports links...
1184
			 */
1185
			$this->resetExportsLinks();
1186
			$this->redrawControl('exports');
1187
1188
			foreach ($snippets as $snippet) {
1189
				$this->redrawControl($snippet);
1190
			}
1191
1192
			$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...
1193
		} else {
1194
			$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...
1195
		}
1196
	}
1197
1198
1199
	/**
1200
	 * Redraw just one row via ajax
1201
	 * @param  int   $id
1202
	 * @param  mixed $primary_where_column
1203
	 * @return void
1204
	 */
1205
	public function redrawItem($id, $primary_where_column = NULL)
1206
	{
1207
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1208
1209
		$this->redrawControl('items');
1210
		$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...
1211
	}
1212
1213
1214
	/********************************************************************************
1215
	 *                                  Components                                  *
1216
	 ********************************************************************************/
1217
1218
1219
	/**
1220
	 * Paginator factory
1221
	 * @return Components\DataGridPaginator\DataGridPaginator
1222
	 */
1223
	public function createComponentPaginator()
1224
	{
1225
		/**
1226
		 * Init paginator
1227
		 */
1228
		$component = new Components\DataGridPaginator\DataGridPaginator;
1229
		$paginator = $component->getPaginator();
1230
1231
		$paginator->setPage($this->page);
1232
		$paginator->setItemsPerPage($this->getPerPage());
1233
1234
		return $component;
1235
	}
1236
1237
1238
	/**
1239
	 * PerPage form factory
1240
	 * @return Form
1241
	 */
1242
	public function createComponentPerPage()
1243
	{
1244
		$form = new Form;
1245
1246
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1247
			->setValue($this->getPerPage());
1248
1249
		$form->addSubmit('submit', '');
1250
1251
		$saveSessionData = [$this, 'saveSessionData'];
1252
1253
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1254
			/**
1255
			 * Session stuff
1256
			 */
1257
			$saveSessionData('_grid_per_page', $values->per_page);
1258
1259
			/**
1260
			 * Other stuff
1261
			 */
1262
			$this->per_page = $values->per_page;
1263
			$this->reload();
1264
		};
1265
1266
		return $form;
1267
	}
1268
1269
1270
	/**
1271
	 * FilterAndGroupAction form factory
1272
	 * @return Form
1273
	 */
1274
	public function createComponentFilter()
1275
	{
1276
		$form = new Form($this, 'filter');
1277
1278
		$form->setMethod('get');
1279
1280
		/**
1281
		 * Filter part
1282
		 */
1283
		$filter_container = $form->addContainer('filter');
1284
1285
		foreach ($this->filters as $filter) {
1286
			$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...
1287
		}
1288
1289
		/**
1290
		 * Group action part
1291
		 */
1292
		$group_action_container = $form->addContainer('group_action');
1293
1294
		if ($this->hasGroupActions()) {
1295
			$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator());
1296
		}
1297
1298
		$form->setDefaults(['filter' => $this->filter]);
1299
1300
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1301
1302
		return $form;
1303
	}
1304
1305
1306
	/**
1307
	 * Set $this->filter values after filter form submitted
1308
	 * @param  Form $form
1309
	 * @return void
1310
	 */
1311
	public function filterSucceeded(Form $form)
1312
	{
1313
		$values = $form->getValues();
1314
1315
		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...
1316
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1317
				return;
1318
			}
1319
		}
1320
1321
		$values = $values['filter'];
1322
1323
		foreach ($values as $key => $value) {
1324
			/**
1325
			 * Session stuff
1326
			 */
1327
			$this->saveSessionData($key, $value);
1328
1329
			/**
1330
			 * Other stuff
1331
			 */
1332
			$this->filter[$key] = $value;
1333
		}
1334
1335
		$this->reload();
1336
	}
1337
1338
1339
	/********************************************************************************
1340
	 *                               Support functions                              *
1341
	 ********************************************************************************/
1342
1343
1344
	public function resetExportsLinks()
1345
	{
1346
		foreach ($this->exports as $id => $export) {
1347
			$export->setLink($this->link('export!', ['id' => $id]));
1348
		}
1349
	}
1350
1351
1352
	/**
1353
	 * Get parameter per_page
1354
	 * @return int
1355
	 */
1356
	public function getPerPage()
1357
	{
1358
		$per_page = $this->per_page ?: reset($this->items_per_page_list);
1359
1360
		if ($per_page !== 'all' && !in_array($this->per_page, $this->items_per_page_list)) {
1361
			$per_page = reset($this->items_per_page_list);
1362
		}
1363
1364
		return $per_page;
1365
	}
1366
1367
1368
	/**
1369
	 * Get associative array of items_per_page_list
1370
	 * @return array
1371
	 */
1372
	public function getItemsPerPageList()
1373
	{
1374
		$list = array_flip($this->items_per_page_list);
1375
1376
		foreach ($list as $key => $value) {
1377
			$list[$key] = $key;
1378
		}
1379
1380
		$list['all'] = $this->getTranslator()->translate('Vše');
1381
1382
		return $list;
1383
	}
1384
1385
1386
	/**
1387
	 * Get primary key of datagrid data source
1388
	 * @return string
1389
	 */
1390
	public function getPrimaryKey()
1391
	{
1392
		return $this->primary_key;
1393
	}
1394
1395
1396
	/**
1397
	 * Get set of set columns
1398
	 * @return Column\IColumn[]
1399
	 */
1400
	public function getColumns()
1401
	{
1402
		return $this->columns;
1403
	}
1404
1405
1406
	/**
1407
	 * Has datagrid some group actions?
1408
	 * @return boolean
1409
	 */
1410
	public function hasGroupActions()
1411
	{
1412
		return (bool) $this->group_action_collection;
1413
	}
1414
1415
1416
	/**
1417
	 * Get translator for datagrid
1418
	 * @return Nette\Localization\ITranslator
1419
	 */
1420
	public function getTranslator()
1421
	{
1422
		if (!$this->translator) {
1423
			$this->translator = new Localization\SimpleTranslator;
1424
		}
1425
1426
		return $this->translator;
1427
	}
1428
1429
1430
	/**
1431
	 * Set datagrid translator
1432
	 * @param Nette\Localization\ITranslator $translator
1433
	 */
1434
	public function setTranslator(Nette\Localization\ITranslator $translator)
1435
	{
1436
		$this->translator = $translator;
1437
1438
		return $this;
1439
	}
1440
1441
1442
	/**
1443
	 * Should be datagrid filters rendered separately?
1444
	 * @param boolean $out
1445
	 */
1446
	public function setOuterFilterRendering($out = TRUE)
1447
	{
1448
		$this->outer_filter_rendering = (bool) $out;
1449
	}
1450
1451
1452
	/**
1453
	 * Are datagrid filters rendered separately?
1454
	 * @return boolean
1455
	 */
1456
	public function hasOuterFilterRendering()
1457
	{
1458
		return $this->outer_filter_rendering;
1459
	}
1460
1461
1462
	/**
1463
	 * Set order of datagrid columns
1464
	 * @param array $order
1465
	 */
1466
	public function setColumnsOrder($order)
1467
	{
1468
		$new_order = [];
1469
1470
		foreach ($order as $key) {
1471
			if (isset($this->columns[$key])) {
1472
				$new_order[$key] = $this->columns[$key];
1473
			}
1474
		}
1475
1476
		if (sizeof($new_order) === sizeof($this->columns)) {
1477
			$this->columns = $new_order;
1478
		} else {
1479
			throw new DataGridException('When changing columns order, you have to specify all columns');
1480
		}
1481
	}
1482
1483
1484
	/**
1485
	 * Should datagrid remember its filters/pagination/etc using session?
1486
	 * @param bool $remember
1487
	 */
1488
	public function setRememberState($remember = TRUE)
1489
	{
1490
		$this->remember_state = (bool) $remember;
1491
	}
1492
1493
1494
	/**
1495
	 * Should datagrid refresh url using history API?
1496
	 * @param bool $refresh
1497
	 */
1498
	public function setRefreshUrl($refresh = TRUE)
1499
	{
1500
		$this->refresh_url = (bool) $refresh;
1501
	}
1502
1503
1504
	/**
1505
	 * Get session data if functionality is enabled
1506
	 * @param  string $key
1507
	 * @return mixed
1508
	 */
1509
	public function getSessionData($key = NULL)
1510
	{
1511
		if (!$this->remember_state) {
1512
			return NULL;
1513
		}
1514
1515
		return $key ? $this->grid_session->{$key} : $this->grid_session;
1516
	}
1517
1518
1519
	/**
1520
	 * Save session data - just if it is enabled
1521
	 * @param  string $key
1522
	 * @param  mixed  $value
1523
	 * @return void
1524
	 */
1525
	public function saveSessionData($key, $value)
1526
	{
1527
1528
		if ($this->remember_state) {
1529
			$this->grid_session->{$key} = $value;
1530
		}
1531
	}
1532
1533
1534
	/**
1535
	 * Delete session data
1536
	 * @return void
1537
	 */
1538
	public function deleteSesssionData($key)
1539
	{
1540
		unset($this->grid_session->{$key});
1541
	}
1542
1543
1544
	/**
1545
	 * Get items detail parameters
1546
	 * @return array
1547
	 */
1548
	public function getItemsDetail()
1549
	{
1550
		return $this->items_detail;
1551
	}
1552
1553
1554
	/**
1555
	 * Items can have thair detail - toggled
1556
	 * @param mixed $detail callable|string|bool
1557
	 */
1558
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1559
	{
1560
		if ($this->isSortable()) {
1561
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1562
		}
1563
1564
		$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...
1565
			$this,
1566
			$primary_where_column ?: $this->primary_key
1567
		);
1568
1569
		if (is_string($detail)) {
1570
			/**
1571
			 * Item detail will be in separate template
1572
			 */
1573
			$this->items_detail->setType('template');
1574
			$this->items_detail->setTemplate($detail);
1575
1576
		} else if (is_callable($detail)) {
1577
			/**
1578
			 * Item detail will be rendered via custom callback renderer
1579
			 */
1580
			$this->items_detail->setType('renderer');
1581
			$this->items_detail->setRenderer($detail);
1582
1583
		} else if (TRUE === $detail) {
1584
			/**
1585
			 * Item detail will be rendered probably via block #detail
1586
			 */
1587
			$this->items_detail->setType('block');
1588
1589
		} else {
1590
			throw new DataGridException(
1591
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1592
			);
1593
		}
1594
1595
		return $this->items_detail;
1596
	}
1597
1598
1599
	/**
1600
	 * Get cont of columns
1601
	 * @return int
1602
	 */
1603
	public function getColumnsCount()
1604
	{
1605
		$count = sizeof($this->columns);
1606
1607
		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...
1608
			$count++;
1609
		}
1610
1611
		if ($this->hasGroupActions()) {
1612
			$count++;
1613
		}
1614
1615
		return $count;
1616
	}
1617
1618
1619
	public function allowRowsGroupAction(callable $condition)
1620
	{
1621
		$this->row_conditions['group_action'] = $condition;
1622
	}
1623
1624
1625
	public function allowRowsAction($key, callable $condition)
1626
	{
1627
		$this->row_conditions['action'][$key] = $condition;
1628
	}
1629
1630
1631
	public function getRowCondition($name, $key = NULL)
1632
	{
1633
		if (!isset($this->row_conditions[$name])) {
1634
			return FALSE;
1635
		}
1636
1637
		$condition = $this->row_conditions[$name];
1638
1639
		if (!$key) {
1640
			return $condition;
1641
		}
1642
1643
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1644
	}
1645
1646
}
1647
1648
1649
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...
1650
{
1651
}
1652