Completed
Push — master ( a6f017...7b4440 )
by Pavel
02:41
created

DataGrid::handleEdit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
247
248
			/**
249
			 * Try to find previous filters/pagination/sort in session
250
			 */
251
			$this->findSessionFilters();
252
		}
253
	}
254
255
256
	/********************************************************************************
257
	 *                                  RENDERING                                   *
258
	 ********************************************************************************/
259
260
261
	/**
262
	 * Render template
263
	 * @return void
264
	 */
265
	public function render()
266
	{
267
		/**
268
		 * Check whether datagrid has set some columns, initiated data source, etc
269
		 */
270
		if (!($this->dataModel instanceof DataModel)) {
271
			throw new DataGridException('You have to set a data source first.');
272
		}
273
274
		if (empty($this->columns)) {
275
			throw new DataGridException('You have to add at least one column.');
276
		}
277
278
		$this->template->setTranslator($this->getTranslator());
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
279
280
		/**
281
		 * Invoke some possible events
282
		 */
283
		$this->onRender($this);
0 ignored issues
show
Bug introduced by
The method onRender() does not exist on Ublaboo\DataGrid\DataGrid. Did you maybe mean render()?

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

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

Loading history...
284
285
		/**
286
		 * Prepare data for rendering (datagrid may render just one item)
287
		 */
288
		$rows = [];
289
290
		if (!empty($this->redraw_item)) {
291
			$items = $this->dataModel->filterRow($this->redraw_item);
292
		} else {
293
			$items = Nette\Utils\Callback::invokeArgs(
294
				[$this->dataModel, 'filterData'],
295
				[
296
					$this->getPaginator(),
297
					$this->sort,
298
					$this->assableFilters()
299
				]
300
			);
301
		}
302
303
		$callback = $this->rowCallback ?: NULL;
304
305
		foreach ($items as $item) {
306
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
307
308
			if ($callback) {
309
				$callback($item, $row->getControl());
310
			}
311
		}
312
313
		if ($this->isTreeView()) {
314
			$this->template->tree_view_has_children_column = $this->tree_view_has_children_column;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing tree_view_has_children_column on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
315
		}
316
317
		$this->template->rows = $rows;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing rows on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
318
319
		$this->template->columns = $this->getColumns();
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing columns on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
320
		$this->template->actions = $this->actions;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing actions on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
321
		$this->template->exports = $this->exports;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing exports on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
322
		$this->template->filters = $this->filters;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing filters on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
323
324
		$this->template->filter_active = $this->isFilterActive();
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing filter_active on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
325
		$this->template->original_template = $this->getOriginalTemplateFile();
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing original_template on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
326
		$this->template->icon_prefix = static::$icon_prefix;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing icon_prefix on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
327
		$this->template->items_detail = $this->items_detail;
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing items_detail on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
328
329
		/**
330
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
331
		 */
332
		$this->template->filter = $this['filter'];
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
Accessing filter on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
333
334
		/**
335
		 * Set template file and render it
336
		 */
337
		$this->template->setFile($this->getTemplateFile())->render();
0 ignored issues
show
Documentation introduced by
The property $template is declared private in Nette\Application\UI\Control. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method render cannot be called on $this->template->setFile...his->getTemplateFile()) (of type null).

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

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

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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

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

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

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

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

Loading history...
466
	 */
467
	public function setSortableHandler($handler = 'sort!')
468
	{
469
		$this->sortable_handler = (string) $handler;
470
471
		return $this;
472
	}
473
474
475
	/**
476
	 * Tell whether DataGrid is sortable
477
	 * @return bool
478
	 */
479
	public function isSortable()
480
	{
481
		return $this->sortable;
482
	}
483
484
	/**
485
	 * Return sortable handle name
486
	 * @return string
487
	 */
488
	public function getSortableHandler()
489
	{
490
		return $this->sortable_handler;
491
	}
492
493
494
	/********************************************************************************
495
	 *                                  TREE VIEW                                   *
496
	 ********************************************************************************/
497
498
499
	/**
500
	 * Is tree view set?
501
	 * @return boolean
502
	 */
503
	public function isTreeView()
504
	{
505
		return (bool) $this->tree_view_children_callback;
506
	}
507
508
509
	/**
510
	 * Setting tree view
511
	 * @param callable $get_children_callback
512
	 * @param string $tree_view_has_children_column
513
	 * @return DataGrid
514
	 */
515
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
516
	{
517
		if (!is_callable($get_children_callback)) {
518
			throw new DataGridException(
519
				'Parameters to method DataGrid::setTreeView must be of type callable'
520
			);
521
		}
522
523
		$this->tree_view_children_callback = $get_children_callback;
524
		$this->tree_view_has_children_column = $tree_view_has_children_column;
525
526
		/**
527
		 * TUrn off pagination
528
		 */
529
		$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...
530
531
		/**
532
		 * Set tree view template file
533
		 */
534
		if (!$this->template_file) {
535
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
536
		}
537
538
		return $this;
539
	}
540
541
542
	/********************************************************************************
543
	 *                                    COLUMNS                                   *
544
	 ********************************************************************************/
545
546
547
	/**
548
	 * Add text column with no other formating
549
	 * @param  string      $key
550
	 * @param  string      $name
551
	 * @param  string|null $column
552
	 * @return Column\ColumnText
553
	 */
554
	public function addColumnText($key, $name, $column = NULL)
555
	{
556
		$this->addColumnCheck($key);
557
		$column = $column ?: $key;
558
559
		return $this->columns[$key] = new Column\ColumnText($column, $name);
560
	}
561
562
563
	/**
564
	 * Add column with link
565
	 * @param  string      $key
566
	 * @param  string      $name
567
	 * @param  string|null $column
568
	 * @return Column\ColumnLink
569
	 */
570
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
571
	{
572
		$this->addColumnCheck($key);
573
		$column = $column ?: $key;
574
		$href = $href ?: $key;
575
576
		if (NULL === $params) {
577
			$params = [$this->primary_key];
578
		}
579
580
		return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params);
581
	}
582
583
584
	/**
585
	 * Add column with possible number formating
586
	 * @param  string      $key
587
	 * @param  string      $name
588
	 * @param  string|null $column
589
	 * @return Column\ColumnNumber
590
	 */
591
	public function addColumnNumber($key, $name, $column = NULL)
592
	{
593
		$this->addColumnCheck($key);
594
		$column = $column ?: $key;
595
596
		return $this->columns[$key] = new Column\ColumnNumber($column, $name);
597
	}
598
599
600
	/**
601
	 * Add column with date formating
602
	 * @param  string      $key
603
	 * @param  string      $name
604
	 * @param  string|null $column
605
	 * @return Column\ColumnDateTime
606
	 */
607
	public function addColumnDateTime($key, $name, $column = NULL)
608
	{
609
		$this->addColumnCheck($key);
610
		$column = $column ?: $key;
611
612
		return $this->columns[$key] = new Column\ColumnDateTime($column, $name);
613
	}
614
615
616
	/**
617
	 * Return existing column
618
	 * @param  string $key
619
	 * @return Column\Column
620
	 * @throws DataGridException
621
	 */
622
	public function getColumn($key)
623
	{
624
		if (!isset($this->columns[$key])) {
625
			throw new DataGridException("There is no column at key [$key] defined.");
626
		}
627
628
		return $this->columns[$key];
629
	}
630
631
632
	/**
633
	 * Remove column
634
	 * @param string $key
635
	 * @return void
636
	 */
637
	public function removeColumn($key)
638
	{
639
		unset($this->columns[$key]);
640
	}
641
642
643
	/**
644
	 * Check whether given key already exists in $this->columns
645
	 * @param  string $key
646
	 * @throws DataGridException
647
	 */
648
	protected function addColumnCheck($key)
649
	{
650
		if (isset($this->columns[$key])) {
651
			throw new DataGridException("There is already column at key [$key] defined.");
652
		}
653
	}
654
655
656
	/********************************************************************************
657
	 *                                    ACTIONS                                   *
658
	 ********************************************************************************/
659
660
661
	/**
662
	 * Create action
663
	 * @param string     $key
664
	 * @param string     $name
665
	 * @param string     $href
666
	 * @param array|null $params
667
	 * @return Column\Action
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
	 * @return Filter\FilterText
733
	 * @throws DataGridException
734
	 */
735
	public function addFilterText($key, $name, $columns = NULL)
736
	{
737
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
738
739
		if (!is_array($columns)) {
740
			throw new DataGridException("Filter Text can except only array or string.");
741
		}
742
743
		$this->addFilterCheck($key);
744
745
		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...
746
	}
747
748
749
	/**
750
	 * Add select box filter
751
	 * @param string $key
752
	 * @param string $name
753
	 * @param array  $options
754
	 * @param string $column
755
	 * @return Filter\FilterSelect
756
	 * @throws DataGridException
757
	 */
758 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...
759
	{
760
		$column = $column ?: $key;
761
762
		if (!is_string($column)) {
763
			throw new DataGridException("Filter Select can only filter through one column.");
764
		}
765
766
		$this->addFilterCheck($key);
767
768
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
769
	}
770
771
772
	/**
773
	 * Add datepicker filter
774
	 * @param string $key
775
	 * @param string $name
776
	 * @param string $column
777
	 * @return Filter\FilterDate
778
	 * @throws DataGridException
779
	 */
780
	public function addFilterDate($key, $name, $column = NULL)
781
	{
782
		$column = $column ?: $key;
783
784
		if (!is_string($column)) {
785
			throw new DataGridException("FilterDate can only filter through one column.");
786
		}
787
788
		$this->addFilterCheck($key);
789
790
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
791
	}
792
793
794
	/**
795
	 * Add range filter (from - to)
796
	 * @param string $key
797
	 * @param string $name
798
	 * @param string $column
799
	 * @return Filter\FilterRange
800
	 * @throws DataGridException
801
	 */
802 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...
803
	{
804
		$column = $column ?: $key;
805
806
		if (!is_string($column)) {
807
			throw new DataGridException("FilterRange can only filter through one column.");
808
		}
809
810
		$this->addFilterCheck($key);
811
812
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
813
	}
814
815
816
	/**
817
	 * Add datepicker filter (from - to)
818
	 * @param string $key
819
	 * @param string $name
820
	 * @param string $column
821
	 * @return Filter\FilterDateRange
822
	 * @throws DataGridException
823
	 */
824 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...
825
	{
826
		$column = $column ?: $key;
827
828
		if (!is_string($column)) {
829
			throw new DataGridException("FilterDateRange can only filter through one column.");
830
		}
831
832
		$this->addFilterCheck($key);
833
834
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
835
	}
836
837
838
	/**
839
	 * Check whether given key already exists in $this->filters
840
	 * @param  string $key
841
	 * @throws DataGridException
842
	 */
843
	protected function addFilterCheck($key)
844
	{
845
		if (isset($this->filters[$key])) {
846
			throw new DataGridException("There is already action at key [$key] defined.");
847
		}
848
	}
849
850
851
	/**
852
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
853
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
854
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
855
	 */
856
	public function assableFilters()
857
	{
858
		foreach ($this->filter as $key => $value) {
859
			if (!isset($this->filters[$key])) {
860
				$this->deleteSesssionData($key);
861
862
				continue;
863
			}
864
865
			if (is_array($value) || $value instanceof \Traversable) {
866
				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...
867
					$this->filters[$key]->setValue($value);
868
				}
869
			} else {
870
				if ($value !== '' && $value !== NULL) {
871
					$this->filters[$key]->setValue($value);
872
				}
873
			}
874
		}
875
876
		foreach ($this->columns as $column) {
877
			if (isset($this->sort[$column->getColumnName()])) {
878
				$column->setSort($this->sort);
879
			}
880
		}
881
882
		return $this->filters;
883
	}
884
885
886
	/**
887
	 * Remove filter
888
	 * @param string $key
889
	 * @return void
890
	 */
891
	public function removeFilter($key)
892
	{
893
		unset($this->filters[$key]);
894
	}
895
896
897
	/********************************************************************************
898
	 *                                  FILTERING                                   *
899
	 ********************************************************************************/
900
901
902
	/**
903
	 * Is filter active?
904
	 * @return boolean
905
	 */
906
	public function isFilterActive()
907
	{
908
		$is_filter = ArraysHelper::testTruthy($this->filter);
909
910
		return ($is_filter) || $this->force_filter_active;
911
	}
912
913
914
	/**
915
	 * Tell that filter is active from whatever reasons
916
	 * return static
917
	 */
918
	public function setFilterActive()
919
	{
920
		$this->force_filter_active = TRUE;
921
922
		return $this;
923
	}
924
925
926
	/**
927
	 * If we want to sent some initial filter
928
	 * @param array $filter
929
	 * @return static
930
	 */
931
	public function setFilter(array $filter)
932
	{
933
		$this->filter = $filter;
934
935
		return $this;
936
	}
937
938
939
	/**
940
	 * FilterAndGroupAction form factory
941
	 * @return Form
942
	 */
943
	public function createComponentFilter()
944
	{
945
		$form = new Form($this, 'filter');
946
947
		$form->setMethod('get');
948
949
		/**
950
		 * Filter part
951
		 */
952
		$filter_container = $form->addContainer('filter');
953
954
		foreach ($this->filters as $filter) {
955
			$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...
956
		}
957
958
		/**
959
		 * Group action part
960
		 */
961
		$group_action_container = $form->addContainer('group_action');
962
963
		if ($this->hasGroupActions()) {
964
			$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator());
965
		}
966
967
		$form->setDefaults(['filter' => $this->filter]);
968
969
		$form->onSubmit[] = [$this, 'filterSucceeded'];
970
971
		return $form;
972
	}
973
974
975
	/**
976
	 * Set $this->filter values after filter form submitted
977
	 * @param  Form $form
978
	 * @return void
979
	 */
980
	public function filterSucceeded(Form $form)
981
	{
982
		$values = $form->getValues();
983
984
		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...
985
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
986
				return;
987
			}
988
		}
989
990
		$values = $values['filter'];
991
992
		foreach ($values as $key => $value) {
993
			/**
994
			 * Session stuff
995
			 */
996
			$this->saveSessionData($key, $value);
997
998
			/**
999
			 * Other stuff
1000
			 */
1001
			$this->filter[$key] = $value;
1002
		}
1003
1004
		$this->reload();
1005
	}
1006
1007
1008
	/**
1009
	 * Should be datagrid filters rendered separately?
1010
	 * @param boolean $out
1011
	 * @return static
1012
	 */
1013
	public function setOuterFilterRendering($out = TRUE)
1014
	{
1015
		$this->outer_filter_rendering = (bool) $out;
1016
1017
		return $this;
1018
	}
1019
1020
1021
	/**
1022
	 * Are datagrid filters rendered separately?
1023
	 * @return boolean
1024
	 */
1025
	public function hasOuterFilterRendering()
1026
	{
1027
		return $this->outer_filter_rendering;
1028
	}
1029
1030
1031
	/**
1032
	 * Try to restore session stuff
1033
	 * @return void
1034
	 */
1035
	public function findSessionFilters()
1036
	{
1037
		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...
1038
			return;
1039
		}
1040
1041
		if (!$this->remember_state) {
1042
			return;
1043
		}
1044
1045
		if ($page = $this->getSessionData('_grid_page')) {
1046
			$this->page = $page;
1047
		}
1048
1049
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1050
			$this->per_page = $per_page;
1051
		}
1052
1053
		if ($sort = $this->getSessionData('_grid_sort')) {
1054
			$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...
1055
		}
1056
1057
		foreach ($this->getSessionData() as $key => $value) {
1058
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1059
				$this->filter[$key] = $value;
1060
			}
1061
		}
1062
	}
1063
1064
1065
	/********************************************************************************
1066
	 *                                    EXPORTS                                   *
1067
	 ********************************************************************************/
1068
1069
1070
	/**
1071
	 * Add export of type callback
1072
	 * @param string $text
1073
	 * @param callable $callback
1074
	 * @param boolean $filtered
1075
	 * @return Export\Export
1076
	 */
1077
	public function addExportCallback($text, $callback, $filtered = FALSE)
1078
	{
1079
		if (!is_callable($callback)) {
1080
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1081
		}
1082
1083
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1084
	}
1085
1086
1087
	/**
1088
	 * Add already implemented csv export
1089
	 * @param string $text
1090
	 * @param string $csv_file_name
1091
	 * @return Export\Export
1092
	 */
1093
	public function addExportCsv($text, $csv_file_name)
1094
	{
1095
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1096
	}
1097
1098
1099
	/**
1100
	 * Add already implemented csv export, but for filtered data
1101
	 * @param string $text
1102
	 * @param string $csv_file_name
1103
	 * @return Export\Export
1104
	 */
1105
	public function addExportCsvFiltered($text, $csv_file_name)
1106
	{
1107
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1108
	}
1109
1110
1111
	/**
1112
	 * Add export to array
1113
	 * @param Export\Export $export
1114
	 * @return Export\Export
1115
	 */
1116
	protected function addToExports(Export\Export $export)
1117
	{
1118
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1119
1120
		$export->setLink($this->link('export!', ['id' => $id]));
1121
1122
		return $this->exports[$id] = $export;
1123
	}
1124
1125
1126
	public function resetExportsLinks()
1127
	{
1128
		foreach ($this->exports as $id => $export) {
1129
			$export->setLink($this->link('export!', ['id' => $id]));
1130
		}
1131
	}
1132
1133
1134
	/********************************************************************************
1135
	 *                                 GROUP ACTIONS                                *
1136
	 ********************************************************************************/
1137
1138
1139
	/**
1140
	 * Add group actino
1141
	 * @param string $title
1142
	 * @param array  $options
1143
	 * @return GroupAction\GroupAction
1144
	 */
1145
	public function addGroupAction($title, $options = [])
1146
	{
1147
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1148
	}
1149
1150
1151
	/**
1152
	 * Get collection of all group actions
1153
	 * @return GroupAction\GroupActionCollection
1154
	 */
1155
	public function getGroupActionCollection()
1156
	{
1157
		if (!$this->group_action_collection) {
1158
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1159
		}
1160
1161
		return $this->group_action_collection;
1162
	}
1163
1164
1165
	/**
1166
	 * Has datagrid some group actions?
1167
	 * @return boolean
1168
	 */
1169
	public function hasGroupActions()
1170
	{
1171
		return (bool) $this->group_action_collection;
1172
	}
1173
1174
1175
	/********************************************************************************
1176
	 *                                   HANDLERS                                   *
1177
	 ********************************************************************************/
1178
1179
1180
	/**
1181
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1182
	 * @param  int  $page
1183
	 * @return void
1184
	 */
1185
	public function handlePage($page)
1186
	{
1187
		/**
1188
		 * Session stuff
1189
		 */
1190
		$this->page = $page;
1191
		$this->saveSessionData('_grid_page', $page);
1192
1193
		$this->reload(['table']);
1194
	}
1195
1196
1197
	/**
1198
	 * Handler for sorting
1199
	 * @param array $sort
1200
	 * @return void
1201
	 */
1202
	public function handleSort(array $sort)
1203
	{
1204
		/**
1205
		 * Session stuff
1206
		 */
1207
		$this->sort = $sort;
1208
		$this->saveSessionData('_grid_sort', $this->sort);
1209
1210
		$this->reload(['table']);
1211
	}
1212
1213
1214
	/**
1215
	 * handler for reseting the filter
1216
	 * @return void
1217
	 */
1218
	public function handleResetFilter()
1219
	{
1220
		/**
1221
		 * Session stuff
1222
		 */
1223
		$this->deleteSesssionData('_grid_page');
1224
1225
		foreach ($this->getSessionData() as $key => $value) {
1226
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1227
				$this->deleteSesssionData($key);
1228
			}
1229
		}
1230
1231
		$this->filter = [];
1232
1233
		$this->reload(['grid']);
1234
	}
1235
1236
1237
	/**
1238
	 * Handler for export
1239
	 * @param  int $id Key for particular export class in array $this->exports
1240
	 * @return void
1241
	 */
1242
	public function handleExport($id)
1243
	{
1244
		if (!isset($this->exports[$id])) {
1245
			throw new Nette\Application\ForbiddenRequestException;
1246
		}
1247
1248
		if ($this->columns_export_order) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->columns_export_order of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1249
			$this->setColumnsOrder($this->columns_export_order);
1250
		}
1251
1252
		$export = $this->exports[$id];
1253
1254
		if ($export->isFiltered()) {
1255
			$sort      = $this->sort;
1256
			$filter    = $this->assableFilters();
1257
		} else {
1258
			$sort      = $this->primary_key;
1259
			$filter    = [];
1260
		}
1261
1262
		if (NULL === $this->dataModel) {
1263
			throw new DataGridException('You have to set a data source first.');
1264
		}
1265
1266
		$rows = [];
1267
1268
		$items = Nette\Utils\Callback::invokeArgs(
1269
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1270
		);
1271
1272
		foreach ($items as $item) {
1273
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1274
		}
1275
1276
		if ($export instanceof Export\ExportCsv) {
1277
			$export->invoke($rows, $this);
1278
		} else {
1279
			$export->invoke($items, $this);
1280
		}
1281
1282
		if ($export->isAjax()) {
1283
			$this->reload();
1284
		}
1285
	}
1286
1287
1288
	/**
1289
	 * Handler for getting children of parent item (e.g. category)
1290
	 * @param  int $parent
1291
	 * @return void
1292
	 */
1293 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...
1294
	{
1295
		$this->setDataSource(
1296
			call_user_func($this->tree_view_children_callback, $parent)
1297
		);
1298
1299
		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...
1300
			$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...
1301
			$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...
1302
1303
			$this->redrawControl('items');
1304
1305
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1306
		} else {
1307
			$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...
1308
		}
1309
	}
1310
1311
1312
	/**
1313
	 * Handler for getting item detail
1314
	 * @param  mixed $id
1315
	 * @return void
1316
	 */
1317 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...
1318
	{
1319
		$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...
1320
		$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...
1321
1322
		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...
1323
			$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...
1324
			$this->redrawControl('items');
1325
1326
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1327
		} else {
1328
			$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...
1329
		}
1330
	}
1331
1332
1333
	/**
1334
	 * Handler for inline editing
1335
	 * @param  mixed $id
1336
	 * @param  mixed $key
1337
	 * @return void
1338
	 */
1339
	public function handleEdit($id, $key)
1340
	{
1341
		$column = $this->getColumn($key);
1342
		$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...
1343
1344
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1345
	}
1346
1347
1348
	/**
1349
	 * Redraw $this
1350
	 * @return void
1351
	 */
1352
	public function reload($snippets = [])
1353
	{
1354
		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...
1355
			$this->redrawControl('tbody');
1356
			$this->redrawControl('pagination');
1357
1358
			/**
1359
			 * manualy reset exports links...
1360
			 */
1361
			$this->resetExportsLinks();
1362
			$this->redrawControl('exports');
1363
1364
			foreach ($snippets as $snippet) {
1365
				$this->redrawControl($snippet);
1366
			}
1367
1368
			$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...
1369
1370
			$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1371
		} else {
1372
			$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...
1373
		}
1374
	}
1375
1376
1377
	/**
1378
	 * Redraw just one row via ajax
1379
	 * @param  int   $id
1380
	 * @param  mixed $primary_where_column
1381
	 * @return void
1382
	 */
1383
	public function redrawItem($id, $primary_where_column = NULL)
1384
	{
1385
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1386
1387
		$this->redrawControl('items');
1388
		$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...
1389
1390
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1391
	}
1392
1393
1394
	/**
1395
	 * Tell datagrid to display all columns
1396
	 * @return void
1397
	 */
1398
	public function handleShowAllColumns()
1399
	{
1400
		$this->deleteSesssionData('_grid_hidden_columns');
1401
1402
		$this->redrawControl();
1403
1404
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1405
	}
1406
1407
1408
	/**
1409
	 * Notice datagrid to not display particular columns
1410
	 * @param  string $column
1411
	 * @return void
1412
	 */
1413
	public function handleHideColumn($column)
1414
	{
1415
		/**
1416
		 * Store info about hiding a column to session
1417
		 */
1418
		$columns = $this->getSessionData('_grid_hidden_columns');
1419
1420
		if (empty($columns)) {
1421
			$columns = [$column];
1422
		} else if (!in_array($column, $columns)) {
1423
			array_push($columns, $column);
1424
		}
1425
1426
		$this->saveSessionData('_grid_hidden_columns', $columns);
1427
1428
		$this->redrawControl();
1429
1430
		$this->onRedraw();
0 ignored issues
show
Documentation Bug introduced by
The method onRedraw does not exist on object<Ublaboo\DataGrid\DataGrid>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

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

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1756
		);
1757
1758
		if (is_string($detail)) {
1759
			/**
1760
			 * Item detail will be in separate template
1761
			 */
1762
			$this->items_detail->setType('template');
1763
			$this->items_detail->setTemplate($detail);
1764
1765
		} else if (is_callable($detail)) {
1766
			/**
1767
			 * Item detail will be rendered via custom callback renderer
1768
			 */
1769
			$this->items_detail->setType('renderer');
1770
			$this->items_detail->setRenderer($detail);
1771
1772
		} else if (TRUE === $detail) {
1773
			/**
1774
			 * Item detail will be rendered probably via block #detail
1775
			 */
1776
			$this->items_detail->setType('block');
1777
1778
		} else {
1779
			throw new DataGridException(
1780
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1781
			);
1782
		}
1783
1784
		return $this->items_detail;
1785
	}
1786
1787
1788
	/********************************************************************************
1789
	 *                                ROW PRIVILEGES                                *
1790
	 ********************************************************************************/
1791
1792
1793
	public function allowRowsGroupAction(callable $condition)
1794
	{
1795
		$this->row_conditions['group_action'] = $condition;
1796
	}
1797
1798
1799
	public function allowRowsAction($key, callable $condition)
1800
	{
1801
		$this->row_conditions['action'][$key] = $condition;
1802
	}
1803
1804
1805
	public function getRowCondition($name, $key = NULL)
1806
	{
1807
		if (!isset($this->row_conditions[$name])) {
1808
			return FALSE;
1809
		}
1810
1811
		$condition = $this->row_conditions[$name];
1812
1813
		if (!$key) {
1814
			return $condition;
1815
		}
1816
1817
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1818
	}
1819
1820
1821
	/********************************************************************************
1822
	 *                               HIDEABLE COLUMNS                               *
1823
	 ********************************************************************************/
1824
1825
1826
	/**
1827
	 * Can datagrid hide colums?
1828
	 * @return boolean
1829
	 */
1830
	public function canHideColumns()
1831
	{
1832
		return (bool) $this->can_hide_columns;
1833
	}
1834
1835
1836
	/**
1837
	 * Order Grid to set columns hideable.
1838
	 * @param bool $do
0 ignored issues
show
Bug introduced by
There is no parameter named $do. Was it maybe removed?

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

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

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

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

Loading history...
1839
	 * @return static
1840
	 */
1841
	public function setColumnsHideable()
1842
	{
1843
		$this->can_hide_columns = TRUE;
1844
1845
		return $this;
1846
	}
1847
1848
1849
	/********************************************************************************
1850
	 *                                   INTERNAL                                   *
1851
	 ********************************************************************************/
1852
1853
1854
	/**
1855
	 * Get cont of columns
1856
	 * @return int
1857
	 */
1858
	public function getColumnsCount()
1859
	{
1860
		$count = sizeof($this->getColumns());
1861
1862
		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...
1863
			$count++;
1864
		}
1865
1866
		if ($this->hasGroupActions()) {
1867
			$count++;
1868
		}
1869
1870
		return $count;
1871
	}
1872
1873
1874
	/**
1875
	 * Get primary key of datagrid data source
1876
	 * @return string
1877
	 */
1878
	public function getPrimaryKey()
1879
	{
1880
		return $this->primary_key;
1881
	}
1882
1883
1884
	/**
1885
	 * Get set of set columns
1886
	 * @return Column\IColumn[]
1887
	 */
1888
	public function getColumns()
1889
	{
1890
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
1891
			$this->removeColumn($column);
1892
		}
1893
1894
		return $this->columns;
1895
	}
1896
1897
1898
1899
	/**
1900
	 * @return PresenterComponent
1901
	 */
1902
	public function getParent()
1903
	{
1904
		$parent = parent::getParent();
1905
		if (!$parent instanceof PresenterComponent) {
1906
			throw new DataGridHasToBeAttachedToPresenterComponentException(
1907
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
1908
			);
1909
		}
1910
1911
		return $parent;
1912
	}
1913
1914
}
1915