Completed
Push — master ( f133b3...91255a )
by Pavel
03:38
created

DataGrid::useHappyComponents()   A

Complexity

Conditions 2
Paths 2

Size

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

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

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

Loading history...
1591
			$this->setItemsPerPageList([10, 20, 50], TRUE);
1592
		}
1593
1594
		$list = array_flip($this->items_per_page_list);
1595
1596
		foreach ($list as $key => $value) {
1597
			$list[$key] = $key;
1598
		}
1599
1600
		if (array_key_exists('all', $list)) {
1601
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
1602
		}
1603
1604
		return $list;
1605
	}
1606
1607
1608
	/**
1609
	 * Order Grid to "be paginated"
1610
	 * @param bool $do
1611
	 * @return static
1612
	 */
1613
	public function setPagination($do)
1614
	{
1615
		$this->do_paginate = (bool) $do;
1616
1617
		return $this;
1618
	}
1619
1620
1621
	/**
1622
	 * Tell whether Grid is paginated
1623
	 * @return bool
1624
	 */
1625
	public function isPaginated()
1626
	{
1627
		return $this->do_paginate;
1628
	}
1629
1630
1631
	/**
1632
	 * Return current paginator class
1633
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1634
	 */
1635
	public function getPaginator()
1636
	{
1637
		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...
1638
			return $this['paginator'];
1639
		}
1640
1641
		return NULL;
1642
	}
1643
1644
1645
	/********************************************************************************
1646
	 *                                     I18N                                     *
1647
	 ********************************************************************************/
1648
1649
1650
	/**
1651
	 * Set datagrid translator
1652
	 * @param Nette\Localization\ITranslator $translator
1653
	 * @return static
1654
	 */
1655
	public function setTranslator(Nette\Localization\ITranslator $translator)
1656
	{
1657
		$this->translator = $translator;
1658
1659
		return $this;
1660
	}
1661
1662
1663
	/**
1664
	 * Get translator for datagrid
1665
	 * @return Nette\Localization\ITranslator
1666
	 */
1667
	public function getTranslator()
1668
	{
1669
		if (!$this->translator) {
1670
			$this->translator = new Localization\SimpleTranslator;
1671
		}
1672
1673
		return $this->translator;
1674
	}
1675
1676
1677
	/********************************************************************************
1678
	 *                                 COLUMNS ORDER                                *
1679
	 ********************************************************************************/
1680
1681
1682
	/**
1683
	 * Set order of datagrid columns
1684
	 * @param array $order
1685
	 * @return static
1686
	 */
1687
	public function setColumnsOrder($order)
1688
	{
1689
		$new_order = [];
1690
1691
		foreach ($order as $key) {
1692
			if (isset($this->columns[$key])) {
1693
				$new_order[$key] = $this->columns[$key];
1694
			}
1695
		}
1696
1697
		if (sizeof($new_order) === sizeof($this->columns)) {
1698
			$this->columns = $new_order;
1699
		} else {
1700
			throw new DataGridException('When changing columns order, you have to specify all columns');
1701
		}
1702
1703
		return $this;
1704
	}
1705
1706
1707
	/**
1708
	 * Columns order may be different for export and normal grid
1709
	 * @param array $order
1710
	 */
1711
	public function setColumnsExportOrder($order)
1712
	{
1713
		$this->columns_export_order = (array) $order;
1714
	}
1715
1716
1717
	/********************************************************************************
1718
	 *                                SESSION & URL                                 *
1719
	 ********************************************************************************/
1720
1721
1722
	/**
1723
	 * Find some unique session key name
1724
	 * @return string
1725
	 */
1726
	public function getSessionSectionName()
1727
	{
1728
		return $this->getPresenter()->getName().':'.$this->getName();
1729
	}
1730
1731
1732
	/**
1733
	 * Should datagrid remember its filters/pagination/etc using session?
1734
	 * @param bool $remember
1735
	 * @return static
1736
	 */
1737
	public function setRememberState($remember = TRUE)
1738
	{
1739
		$this->remember_state = (bool) $remember;
1740
1741
		return $this;
1742
	}
1743
1744
1745
	/**
1746
	 * Should datagrid refresh url using history API?
1747
	 * @param bool $refresh
1748
	 * @return static
1749
	 */
1750
	public function setRefreshUrl($refresh = TRUE)
1751
	{
1752
		$this->refresh_url = (bool) $refresh;
1753
1754
1755
		return $this;
1756
	}
1757
1758
1759
	/**
1760
	 * Get session data if functionality is enabled
1761
	 * @param  string $key
1762
	 * @return mixed
1763
	 */
1764
	public function getSessionData($key = NULL, $default_value = NULL)
1765
	{
1766
		if (!$this->remember_state) {
1767
			return $key ? $default_value : [];
1768
		}
1769
1770
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
1771
	}
1772
1773
1774
	/**
1775
	 * Save session data - just if it is enabled
1776
	 * @param  string $key
1777
	 * @param  mixed  $value
1778
	 * @return void
1779
	 */
1780
	public function saveSessionData($key, $value)
1781
	{
1782
		if ($this->remember_state) {
1783
			$this->grid_session->{$key} = $value;
1784
		}
1785
	}
1786
1787
1788
	/**
1789
	 * Delete session data
1790
	 * @return void
1791
	 */
1792
	public function deleteSesssionData($key)
1793
	{
1794
		unset($this->grid_session->{$key});
1795
	}
1796
1797
1798
	/********************************************************************************
1799
	 *                                  ITEM DETAIL                                 *
1800
	 ********************************************************************************/
1801
1802
1803
	/**
1804
	 * Get items detail parameters
1805
	 * @return array
1806
	 */
1807
	public function getItemsDetail()
1808
	{
1809
		return $this->items_detail;
1810
	}
1811
1812
1813
	/**
1814
	 * Items can have thair detail - toggled
1815
	 * @param mixed $detail callable|string|bool
1816
	 * @param bool|NULL $primary_where_column
1817
	 * @return Column\ItemDetail
1818
	 */
1819
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1820
	{
1821
		if ($this->isSortable()) {
1822
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1823
		}
1824
1825
		$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...
1826
			$this,
1827
			$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...
1828
		);
1829
1830
		if (is_string($detail)) {
1831
			/**
1832
			 * Item detail will be in separate template
1833
			 */
1834
			$this->items_detail->setType('template');
1835
			$this->items_detail->setTemplate($detail);
1836
1837
		} else if (is_callable($detail)) {
1838
			/**
1839
			 * Item detail will be rendered via custom callback renderer
1840
			 */
1841
			$this->items_detail->setType('renderer');
1842
			$this->items_detail->setRenderer($detail);
1843
1844
		} else if (TRUE === $detail) {
1845
			/**
1846
			 * Item detail will be rendered probably via block #detail
1847
			 */
1848
			$this->items_detail->setType('block');
1849
1850
		} else {
1851
			throw new DataGridException(
1852
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1853
			);
1854
		}
1855
1856
		return $this->items_detail;
1857
	}
1858
1859
1860
	/********************************************************************************
1861
	 *                                ROW PRIVILEGES                                *
1862
	 ********************************************************************************/
1863
1864
1865
	public function allowRowsGroupAction(callable $condition)
1866
	{
1867
		$this->row_conditions['group_action'] = $condition;
1868
	}
1869
1870
1871
	public function allowRowsAction($key, callable $condition)
1872
	{
1873
		$this->row_conditions['action'][$key] = $condition;
1874
	}
1875
1876
1877
	public function getRowCondition($name, $key = NULL)
1878
	{
1879
		if (!isset($this->row_conditions[$name])) {
1880
			return FALSE;
1881
		}
1882
1883
		$condition = $this->row_conditions[$name];
1884
1885
		if (!$key) {
1886
			return $condition;
1887
		}
1888
1889
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1890
	}
1891
1892
1893
	/********************************************************************************
1894
	 *                               HIDEABLE COLUMNS                               *
1895
	 ********************************************************************************/
1896
1897
1898
	/**
1899
	 * Can datagrid hide colums?
1900
	 * @return boolean
1901
	 */
1902
	public function canHideColumns()
1903
	{
1904
		return (bool) $this->can_hide_columns;
1905
	}
1906
1907
1908
	/**
1909
	 * Order Grid to set columns hideable.
1910
	 * @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...
1911
	 * @return static
1912
	 */
1913
	public function setColumnsHideable()
1914
	{
1915
		$this->can_hide_columns = TRUE;
1916
1917
		return $this;
1918
	}
1919
1920
1921
	/********************************************************************************
1922
	 *                                   INTERNAL                                   *
1923
	 ********************************************************************************/
1924
1925
1926
	/**
1927
	 * Get cont of columns
1928
	 * @return int
1929
	 */
1930
	public function getColumnsCount()
1931
	{
1932
		$count = sizeof($this->getColumns());
1933
1934
		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...
1935
			$count++;
1936
		}
1937
1938
		if ($this->hasGroupActions()) {
1939
			$count++;
1940
		}
1941
1942
		return $count;
1943
	}
1944
1945
1946
	/**
1947
	 * Get primary key of datagrid data source
1948
	 * @return string
1949
	 */
1950
	public function getPrimaryKey()
1951
	{
1952
		return $this->primary_key;
1953
	}
1954
1955
1956
	/**
1957
	 * Get set of set columns
1958
	 * @return Column\IColumn[]
1959
	 */
1960
	public function getColumns()
1961
	{
1962
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
1963
			$this->removeColumn($column);
1964
		}
1965
1966
		return $this->columns;
1967
	}
1968
1969
1970
1971
	/**
1972
	 * @return PresenterComponent
1973
	 */
1974
	public function getParent()
1975
	{
1976
		$parent = parent::getParent();
1977
		if (!$parent instanceof PresenterComponent) {
1978
			throw new DataGridHasToBeAttachedToPresenterComponentException(
1979
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
1980
			);
1981
		}
1982
1983
		return $parent;
1984
	}
1985
1986
}
1987