Completed
Pull Request — master (#83)
by Petr
03:06
created

DataGrid::getParent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

This error could be the result of:

1. Missing dependencies

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

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

2. Missing use statement

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

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

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

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

This error could be the result of:

1. Missing dependencies

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

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

2. Missing use statement

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

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

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

Loading history...
407
			$data_source = new DataSource\DoctrineDataSource($source, $this->primary_key);
408
409
		} else {
410
			$data_source_class = $source ? get_class($source) : 'NULL';
411
			throw new DataGridException("DataGrid can not take [$data_source_class] as data source.");
412
		}
413
414
		$this->dataModel = new DataModel($data_source);
415
416
		return $this;
417
	}
418
419
420
	/********************************************************************************
421
	 *                                  TEMPLATING                                  *
422
	 ********************************************************************************/
423
424
425
	/**
426
	 * Set custom template file to render
427
	 * @param string $template_file
428
	 */
429
	public function setTemplateFile($template_file)
430
	{
431
		$this->template_file = $template_file;
432
433
		return $this;
434
	}
435
436
437
	/**
438
	 * Get DataGrid template file
439
	 * @return string
440
	 */
441
	public function getTemplateFile()
442
	{
443
		return $this->template_file ?: $this->getOriginalTemplateFile();
444
	}
445
446
447
	/**
448
	 * Get DataGrid original template file
449
	 * @return string
450
	 */
451
	public function getOriginalTemplateFile()
452
	{
453
		return __DIR__.'/templates/datagrid.latte';
454
	}
455
456
457
	/********************************************************************************
458
	 *                                   SORTING                                    *
459
	 ********************************************************************************/
460
461
462
	/**
463
	 * Set default sorting
464
	 * @param aray $sort
465
	 */
466
	public function setDefaultSort($sort)
467
	{
468
		if (empty($this->sort)) {
469
			$this->sort = (array) $sort;
470
471
			$this->saveSessionData('_grid_sort', $this->sort);
472
		}
473
474
		return $this;
475
	}
476
477
478
	/**
479
	 * Set grido to be sortable
480
	 * @param bool $sortable
481
	 */
482
	public function setSortable($sortable = TRUE)
483
	{
484
		if ($this->getItemsDetail()) {
485
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
486
		}
487
488
		$this->sortable = (bool) $sortable;
489
490
		return $this;
491
	}
492
493
494
	/**
495
	 * Set sortable handle
496
	 * @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...
497
	 */
498
	public function setSortableHandler($handler = 'sort!')
499
	{
500
		$this->sortable_handler = (string) $handler;
501
502
		return $this;
503
	}
504
505
506
	/**
507
	 * Tell whether DataGrid is sortable
508
	 * @return bool
509
	 */
510
	public function isSortable()
511
	{
512
		return $this->sortable;
513
	}
514
515
	/**
516
	 * Return sortable handle name
517
	 * @return string
518
	 */
519
	public function getSortableHandler()
520
	{
521
		return $this->sortable_handler;
522
	}
523
524
525
	/********************************************************************************
526
	 *                                  TREE VIEW                                   *
527
	 ********************************************************************************/
528
529
530
	/**
531
	 * Is tree view set?
532
	 * @return boolean
533
	 */
534
	public function isTreeView()
535
	{
536
		return (bool) $this->tree_view_children_callback;
537
	}
538
539
540
	/**
541
	 * Setting tree view
542
	 * @param callable $get_children_callback
543
	 * @param string $tree_view_has_children_column
544
	 * @return DataGrid
545
	 */
546
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
547
	{
548
		if (!is_callable($get_children_callback)) {
549
			throw new DataGridException(
550
				'Parameters to method DataGrid::setTreeView must be of type callable'
551
			);
552
		}
553
554
		$this->tree_view_children_callback = $get_children_callback;
555
		$this->tree_view_has_children_column = $tree_view_has_children_column;
556
557
		/**
558
		 * TUrn off pagination
559
		 */
560
		$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...
561
562
		/**
563
		 * Set tree view template file
564
		 */
565
		if (!$this->template_file) {
566
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
567
		}
568
569
		return $this;
570
	}
571
572
573
	/********************************************************************************
574
	 *                                    COLUMNS                                   *
575
	 ********************************************************************************/
576
577
578
	/**
579
	 * Add text column with no other formating
580
	 * @param  string      $key
581
	 * @param  string      $name
582
	 * @param  string|null $column
583
	 * @return Column\ColumnText
584
	 */
585
	public function addColumnText($key, $name, $column = NULL)
586
	{
587
		$this->addColumnCheck($key);
588
		$column = $column ?: $key;
589
590
		return $this->columns[$key] = new Column\ColumnText($column, $name);
591
	}
592
593
594
	/**
595
	 * Add column with link
596
	 * @param  string      $key
597
	 * @param  string      $name
598
	 * @param  string|null $column
599
	 * @return Column\ColumnLink
600
	 */
601
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
602
	{
603
		$this->addColumnCheck($key);
604
		$column = $column ?: $key;
605
		$href = $href ?: $key;
606
607
		if (NULL === $params) {
608
			$params = [$this->primary_key];
609
		}
610
611
		return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params);
612
	}
613
614
615
	/**
616
	 * Add column with possible number formating
617
	 * @param  string      $key
618
	 * @param  string      $name
619
	 * @param  string|null $column
620
	 * @return Column\ColumnNumber
621
	 */
622
	public function addColumnNumber($key, $name, $column = NULL)
623
	{
624
		$this->addColumnCheck($key);
625
		$column = $column ?: $key;
626
627
		return $this->columns[$key] = new Column\ColumnNumber($column, $name);
628
	}
629
630
631
	/**
632
	 * Add column with date formating
633
	 * @param  string      $key
634
	 * @param  string      $name
635
	 * @param  string|null $column
636
	 * @return Column\ColumnDateTime
637
	 */
638
	public function addColumnDateTime($key, $name, $column = NULL)
639
	{
640
		$this->addColumnCheck($key);
641
		$column = $column ?: $key;
642
643
		return $this->columns[$key] = new Column\ColumnDateTime($column, $name);
644
	}
645
646
647
	/**
648
	 * Return existing column
649
	 * @param  string $key
650
	 * @return Column\Column
651
	 * @throws DataGridException
652
	 */
653
	public function getColumn($key)
654
	{
655
		if (!isset($this->columns[$key])) {
656
			throw new DataGridException("There is no column at key [$key] defined.");
657
		}
658
659
		return $this->columns[$key];
660
	}
661
662
663
	/**
664
	 * Remove column
665
	 * @param string $key
666
	 * @return void
667
	 */
668
	public function removeColumn($key)
669
	{
670
		unset($this->columns[$key]);
671
	}
672
673
674
	/**
675
	 * Check whether given key already exists in $this->columns
676
	 * @param  string $key
677
	 * @throws DataGridException
678
	 */
679
	protected function addColumnCheck($key)
680
	{
681
		if (isset($this->columns[$key])) {
682
			throw new DataGridException("There is already column at key [$key] defined.");
683
		}
684
	}
685
686
687
	/********************************************************************************
688
	 *                                    ACTIONS                                   *
689
	 ********************************************************************************/
690
691
692
	/**
693
	 * Create action
694
	 * @param string     $key
695
	 * @param string     $name
696
	 * @param string     $href
697
	 * @param array|null $params
698
	 */
699
	public function addAction($key, $name = '', $href = NULL, array $params = NULL)
700
	{
701
		$this->addActionCheck($key);
702
		$href = $href ?: $key;
703
704
		if (NULL === $params) {
705
			$params = [$this->primary_key];
706
		}
707
708
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
709
	}
710
711
712
	/**
713
	 * Get existing action
714
	 * @param  string       $key
715
	 * @return Column\Action
716
	 * @throws DataGridException
717
	 */
718
	public function getAction($key)
719
	{
720
		if (!isset($this->actions[$key])) {
721
			throw new DataGridException("There is no action at key [$key] defined.");
722
		}
723
724
		return $this->actions[$key];
725
	}
726
727
728
	/**
729
	 * Remove action
730
	 * @param string $key
731
	 * @return void
732
	 */
733
	public function removeAction($key)
734
	{
735
		unset($this->actions[$key]);
736
	}
737
738
739
	/**
740
	 * Check whether given key already exists in $this->filters
741
	 * @param  string $key
742
	 * @throws DataGridException
743
	 */
744
	protected function addActionCheck($key)
745
	{
746
		if (isset($this->actions[$key])) {
747
			throw new DataGridException("There is already action at key [$key] defined.");
748
		}
749
	}
750
751
752
	/********************************************************************************
753
	 *                                    FILTERS                                   *
754
	 ********************************************************************************/
755
756
757
	/**
758
	 * Add filter fot text search
759
	 * @param string       $key
760
	 * @param string       $name
761
	 * @param array|string $columns
762
	 * @throws DataGridException
763
	 */
764
	public function addFilterText($key, $name, $columns = NULL)
765
	{
766
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
767
768
		if (!is_array($columns)) {
769
			throw new DataGridException("Filter Text can except only array or string.");
770
		}
771
772
		$this->addFilterCheck($key);
773
774
		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...
775
	}
776
777
778
	/**
779
	 * Add select box filter
780
	 * @param string $key
781
	 * @param string $name
782
	 * @param array  $options
783
	 * @param string $column
784
	 * @throws DataGridException
785
	 */
786 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...
787
	{
788
		$column = $column ?: $key;
789
790
		if (!is_string($column)) {
791
			throw new DataGridException("Filter Select can only filter through one column.");
792
		}
793
794
		$this->addFilterCheck($key);
795
796
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
797
	}
798
799
800
	/**
801
	 * Add datepicker filter
802
	 * @param string $key
803
	 * @param string $name
804
	 * @param string $column
805
	 * @throws DataGridException
806
	 */
807
	public function addFilterDate($key, $name, $column = NULL)
808
	{
809
		$column = $column ?: $key;
810
811
		if (!is_string($column)) {
812
			throw new DataGridException("FilterDate can only filter through one column.");
813
		}
814
815
		$this->addFilterCheck($key);
816
817
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
818
	}
819
820
821
	/**
822
	 * Add range filter (from - to)
823
	 * @param string $key
824
	 * @param string $name
825
	 * @param string $column
826
	 * @throws DataGridException
827
	 */
828 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...
829
	{
830
		$column = $column ?: $key;
831
832
		if (!is_string($column)) {
833
			throw new DataGridException("FilterRange can only filter through one column.");
834
		}
835
836
		$this->addFilterCheck($key);
837
838
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
839
	}
840
841
842
	/**
843
	 * Add datepicker filter (from - to)
844
	 * @param string $key
845
	 * @param string $name
846
	 * @param string $column
847
	 * @throws DataGridException
848
	 */
849 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...
850
	{
851
		$column = $column ?: $key;
852
853
		if (!is_string($column)) {
854
			throw new DataGridException("FilterDateRange can only filter through one column.");
855
		}
856
857
		$this->addFilterCheck($key);
858
859
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
860
	}
861
862
863
	/**
864
	 * Check whether given key already exists in $this->filters
865
	 * @param  string $key
866
	 * @throws DataGridException
867
	 */
868
	protected function addFilterCheck($key)
869
	{
870
		if (isset($this->filters[$key])) {
871
			throw new DataGridException("There is already action at key [$key] defined.");
872
		}
873
	}
874
875
876
	/**
877
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
878
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
879
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
880
	 */
881
	public function assableFilters()
882
	{
883
		foreach ($this->filter as $key => $value) {
884
			if (!isset($this->filters[$key])) {
885
				$this->deleteSesssionData($key);
886
887
				continue;
888
			}
889
890
			if (is_array($value) || $value instanceof \Traversable) {
891
				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...
892
					$this->filters[$key]->setValue($value);
893
				}
894
			} else {
895
				if ($value !== '' && $value !== NULL) {
896
					$this->filters[$key]->setValue($value);
897
				}
898
			}
899
		}
900
901
		foreach ($this->columns as $column) {
902
			if (isset($this->sort[$column->getColumnName()])) {
903
				$column->setSort($this->sort);
904
			}
905
		}
906
907
		return $this->filters;
908
	}
909
910
911
	/**
912
	 * Remove filter
913
	 * @param string $key
914
	 * @return void
915
	 */
916
	public function removeFilter($key)
917
	{
918
		unset($this->filters[$key]);
919
	}
920
921
922
	/********************************************************************************
923
	 *                                  FILTERING                                   *
924
	 ********************************************************************************/
925
926
927
	/**
928
	 * Is filter active?
929
	 * @return boolean
930
	 */
931
	public function isFilterActive()
932
	{
933
		$is_filter = ArraysHelper::testTruthy($this->filter);
934
935
		return ($is_filter) || $this->force_filter_active;
936
	}
937
938
939
	/**
940
	 * Tell that filter is active from whatever reasons
941
	 * return static
942
	 */
943
	public function setFilterActive()
944
	{
945
		$this->force_filter_active = TRUE;
946
947
		return $this;
948
	}
949
950
951
	/**
952
	 * If we want to sent some initial filter
953
	 * @param array $filter
954
	 * @return static
955
	 */
956
	public function setFilter(array $filter)
957
	{
958
		$this->filter = $filter;
959
960
		return $this;
961
	}
962
963
964
	/**
965
	 * FilterAndGroupAction form factory
966
	 * @return Form
967
	 */
968
	public function createComponentFilter()
969
	{
970
		$form = new Form($this, 'filter');
971
972
		$form->setMethod('get');
973
974
		/**
975
		 * Filter part
976
		 */
977
		$filter_container = $form->addContainer('filter');
978
979
		foreach ($this->filters as $filter) {
980
			$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...
981
		}
982
983
		/**
984
		 * Group action part
985
		 */
986
		$group_action_container = $form->addContainer('group_action');
987
988
		if ($this->hasGroupActions()) {
989
			$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator());
990
		}
991
992
		$form->setDefaults(['filter' => $this->filter]);
993
994
		$form->onSubmit[] = [$this, 'filterSucceeded'];
995
996
		return $form;
997
	}
998
999
1000
	/**
1001
	 * Set $this->filter values after filter form submitted
1002
	 * @param  Form $form
1003
	 * @return void
1004
	 */
1005
	public function filterSucceeded(Form $form)
1006
	{
1007
		$values = $form->getValues();
1008
1009
		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...
1010
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1011
				return;
1012
			}
1013
		}
1014
1015
		$values = $values['filter'];
1016
1017
		foreach ($values as $key => $value) {
1018
			/**
1019
			 * Session stuff
1020
			 */
1021
			$this->saveSessionData($key, $value);
1022
1023
			/**
1024
			 * Other stuff
1025
			 */
1026
			$this->filter[$key] = $value;
1027
		}
1028
1029
		$this->reload();
1030
	}
1031
1032
1033
	/**
1034
	 * Should be datagrid filters rendered separately?
1035
	 * @param boolean $out
1036
	 * @return static
1037
	 */
1038
	public function setOuterFilterRendering($out = TRUE)
1039
	{
1040
		$this->outer_filter_rendering = (bool) $out;
1041
1042
		return $this;
1043
	}
1044
1045
1046
	/**
1047
	 * Are datagrid filters rendered separately?
1048
	 * @return boolean
1049
	 */
1050
	public function hasOuterFilterRendering()
1051
	{
1052
		return $this->outer_filter_rendering;
1053
	}
1054
1055
1056
	/**
1057
	 * Try to restore session stuff
1058
	 * @return void
1059
	 */
1060
	public function findSessionFilters()
1061
	{
1062
		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...
1063
			return;
1064
		}
1065
1066
		if (!$this->remember_state) {
1067
			return;
1068
		}
1069
1070
		if ($page = $this->getSessionData('_grid_page')) {
1071
			$this->page = $page;
1072
		}
1073
1074
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1075
			$this->per_page = $per_page;
1076
		}
1077
1078
		if ($sort = $this->getSessionData('_grid_sort')) {
1079
			$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...
1080
		}
1081
1082
		foreach ($this->getSessionData() as $key => $value) {
1083
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1084
				$this->filter[$key] = $value;
1085
			}
1086
		}
1087
	}
1088
1089
1090
	/********************************************************************************
1091
	 *                                    EXPORTS                                   *
1092
	 ********************************************************************************/
1093
1094
1095
	/**
1096
	 * Add export of type callback
1097
	 * @param string $text
1098
	 * @param callable $callback
1099
	 * @param boolean $filtered
1100
	 * @return Export\Export
1101
	 */
1102
	public function addExportCallback($text, $callback, $filtered = FALSE)
1103
	{
1104
		if (!is_callable($callback)) {
1105
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1106
		}
1107
1108
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1109
	}
1110
1111
1112
	/**
1113
	 * Add already implemented csv export
1114
	 * @param string $text
1115
	 * @param string $csv_file_name
1116
	 * @return Export\Export
1117
	 */
1118
	public function addExportCsv($text, $csv_file_name)
1119
	{
1120
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1121
	}
1122
1123
1124
	/**
1125
	 * Add already implemented csv export, but for filtered data
1126
	 * @param string $text
1127
	 * @param string $csv_file_name
1128
	 * @return Export\Export
1129
	 */
1130
	public function addExportCsvFiltered($text, $csv_file_name)
1131
	{
1132
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1133
	}
1134
1135
1136
	/**
1137
	 * Add export to array
1138
	 * @param Export\Export $export
1139
	 * @return Export\Export
1140
	 */
1141
	protected function addToExports(Export\Export $export)
1142
	{
1143
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1144
1145
		$export->setLink($this->link('export!', ['id' => $id]));
1146
1147
		return $this->exports[$id] = $export;
1148
	}
1149
1150
1151
	public function resetExportsLinks()
1152
	{
1153
		foreach ($this->exports as $id => $export) {
1154
			$export->setLink($this->link('export!', ['id' => $id]));
1155
		}
1156
	}
1157
1158
1159
	/********************************************************************************
1160
	 *                                 GROUP ACTIONS                                *
1161
	 ********************************************************************************/
1162
1163
1164
	/**
1165
	 * Add group actino
1166
	 * @param string $title
1167
	 * @param array  $options
1168
	 */
1169
	public function addGroupAction($title, $options = [])
1170
	{
1171
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1172
	}
1173
1174
1175
	/**
1176
	 * Get collection of all group actions
1177
	 * @return GroupAction\GroupActionCollection
1178
	 */
1179
	public function getGroupActionCollection()
1180
	{
1181
		if (!$this->group_action_collection) {
1182
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1183
		}
1184
1185
		return $this->group_action_collection;
1186
	}
1187
1188
1189
	/**
1190
	 * Has datagrid some group actions?
1191
	 * @return boolean
1192
	 */
1193
	public function hasGroupActions()
1194
	{
1195
		return (bool) $this->group_action_collection;
1196
	}
1197
1198
1199
	/********************************************************************************
1200
	 *                                   HANDLERS                                   *
1201
	 ********************************************************************************/
1202
1203
1204
	/**
1205
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1206
	 * @param  int  $page
1207
	 * @return void
1208
	 */
1209
	public function handlePage($page)
1210
	{
1211
		/**
1212
		 * Session stuff
1213
		 */
1214
		$this->page = $page;
1215
		$this->saveSessionData('_grid_page', $page);
1216
1217
		$this->reload(['table']);
1218
	}
1219
1220
1221
	/**
1222
	 * Handler for sorting
1223
	 * @param array $sort
1224
	 * @return void
1225
	 */
1226
	public function handleSort(array $sort)
1227
	{
1228
		/**
1229
		 * Session stuff
1230
		 */
1231
		$this->sort = $sort;
1232
		$this->saveSessionData('_grid_sort', $this->sort);
1233
1234
		$this->reload(['table']);
1235
	}
1236
1237
1238
	/**
1239
	 * handler for reseting the filter
1240
	 * @return void
1241
	 */
1242
	public function handleResetFilter()
1243
	{
1244
		/**
1245
		 * Session stuff
1246
		 */
1247
		$this->deleteSesssionData('_grid_page');
1248
1249
		foreach ($this->getSessionData() as $key => $value) {
1250
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1251
				$this->deleteSesssionData($key);
1252
			}
1253
		}
1254
1255
		$this->filter = [];
1256
1257
		$this->reload(['grid']);
1258
	}
1259
1260
1261
	/**
1262
	 * Handler for export
1263
	 * @param  int $id Key for particular export class in array $this->exports
1264
	 * @return void
1265
	 */
1266
	public function handleExport($id)
1267
	{
1268
		if (!isset($this->exports[$id])) {
1269
			throw new Nette\Application\ForbiddenRequestException;
1270
		}
1271
1272
		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...
1273
			$this->setColumnsOrder($this->columns_export_order);
1274
		}
1275
1276
		$export = $this->exports[$id];
1277
1278
		if ($export->isFiltered()) {
1279
			$sort      = $this->sort;
1280
			$filter    = $this->assableFilters();
1281
		} else {
1282
			$sort      = $this->primary_key;
1283
			$filter    = [];
1284
		}
1285
1286
		if (NULL === $this->dataModel) {
1287
			throw new DataGridException('You have to set a data source first.');
1288
		}
1289
1290
		$rows = [];
1291
1292
		$items = Nette\Utils\Callback::invokeArgs(
1293
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1294
		);
1295
1296
		foreach ($items as $item) {
1297
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1298
		}
1299
1300
		if ($export instanceof Export\ExportCsv) {
1301
			$export->invoke($rows, $this);
1302
		} else {
1303
			$export->invoke($items, $this);
1304
		}
1305
1306
		if ($export->isAjax()) {
1307
			$this->reload();
1308
		}
1309
	}
1310
1311
1312
	/**
1313
	 * Handler for getting children of parent item (e.g. category)
1314
	 * @param  int $parent
1315
	 * @return void
1316
	 */
1317 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...
1318
	{
1319
		$this->setDataSource(
1320
			call_user_func($this->tree_view_children_callback, $parent)
1321
		);
1322
1323
		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...
1324
			$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...
1325
			$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...
1326
1327
			$this->redrawControl('items');
1328
1329
			$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...
1330
		} else {
1331
			$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...
1332
		}
1333
	}
1334
1335
1336
	/**
1337
	 * Handler for getting item detail
1338
	 * @param  mixed $id
1339
	 * @return void
1340
	 */
1341 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...
1342
	{
1343
		$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...
1344
		$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...
1345
1346
		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...
1347
			$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...
1348
			$this->redrawControl('items');
1349
1350
			$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...
1351
		} else {
1352
			$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...
1353
		}
1354
	}
1355
1356
1357
	/**
1358
	 * Handler for inline editing
1359
	 * @param  mixed $id
1360
	 * @param  mixed $key
1361
	 * @return void
1362
	 */
1363
	public function handleEdit($id, $key)
1364
	{
1365
		$column = $this->getColumn($key);
1366
		$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...
1367
1368
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1369
	}
1370
1371
1372
	/**
1373
	 * Redraw $this
1374
	 * @return void
1375
	 */
1376
	public function reload($snippets = [])
1377
	{
1378
		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...
1379
			$this->redrawControl('tbody');
1380
			$this->redrawControl('pagination');
1381
1382
			/**
1383
			 * manualy reset exports links...
1384
			 */
1385
			$this->resetExportsLinks();
1386
			$this->redrawControl('exports');
1387
1388
			foreach ($snippets as $snippet) {
1389
				$this->redrawControl($snippet);
1390
			}
1391
1392
			$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...
1393
1394
			$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...
1395
		} else {
1396
			$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...
1397
		}
1398
	}
1399
1400
1401
	/**
1402
	 * Redraw just one row via ajax
1403
	 * @param  int   $id
1404
	 * @param  mixed $primary_where_column
1405
	 * @return void
1406
	 */
1407
	public function redrawItem($id, $primary_where_column = NULL)
1408
	{
1409
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1410
1411
		$this->redrawControl('items');
1412
		$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...
1413
1414
		$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...
1415
	}
1416
1417
1418
	/**
1419
	 * Tell datagrid to display all columns
1420
	 * @return void
1421
	 */
1422
	public function handleShowAllColumns()
1423
	{
1424
		$this->deleteSesssionData('_grid_hidden_columns');
1425
1426
		$this->redrawControl();
1427
1428
		$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...
1429
	}
1430
1431
1432
	/**
1433
	 * Notice datagrid to not display particular columns
1434
	 * @param  string $column
1435
	 * @return void
1436
	 */
1437
	public function handleHideColumn($column)
1438
	{
1439
		/**
1440
		 * Store info about hiding a column to session
1441
		 */
1442
		$columns = $this->getSessionData('_grid_hidden_columns');
1443
1444
		if (empty($columns)) {
1445
			$columns = [$column];
1446
		} else if (!in_array($column, $columns)) {
1447
			array_push($columns, $column);
1448
		}
1449
1450
		$this->saveSessionData('_grid_hidden_columns', $columns);
1451
1452
		$this->redrawControl();
1453
1454
		$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...
1455
	}
1456
1457
1458
	/********************************************************************************
1459
	 *                                  PAGINATION                                  *
1460
	 ********************************************************************************/
1461
1462
1463
	/**
1464
	 * Set options of select "items_per_page"
1465
	 * @param array $items_per_page_list
1466
	 */
1467
	public function setItemsPerPageList(array $items_per_page_list)
1468
	{
1469
		$this->items_per_page_list = $items_per_page_list;
1470
1471
		return $this;
1472
	}
1473
1474
1475
	/**
1476
	 * Paginator factory
1477
	 * @return Components\DataGridPaginator\DataGridPaginator
1478
	 */
1479
	public function createComponentPaginator()
1480
	{
1481
		/**
1482
		 * Init paginator
1483
		 */
1484
		$component = new Components\DataGridPaginator\DataGridPaginator;
1485
		$paginator = $component->getPaginator();
1486
1487
		$paginator->setPage($this->page);
1488
		$paginator->setItemsPerPage($this->getPerPage());
1489
1490
		return $component;
1491
	}
1492
1493
1494
	/**
1495
	 * PerPage form factory
1496
	 * @return Form
1497
	 */
1498
	public function createComponentPerPage()
1499
	{
1500
		$form = new Form;
1501
1502
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1503
			->setValue($this->getPerPage());
1504
1505
		$form->addSubmit('submit', '');
1506
1507
		$saveSessionData = [$this, 'saveSessionData'];
1508
1509
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1510
			/**
1511
			 * Session stuff
1512
			 */
1513
			$saveSessionData('_grid_per_page', $values->per_page);
1514
1515
			/**
1516
			 * Other stuff
1517
			 */
1518
			$this->per_page = $values->per_page;
1519
			$this->reload();
1520
		};
1521
1522
		return $form;
1523
	}
1524
1525
1526
	/**
1527
	 * Get parameter per_page
1528
	 * @return int
1529
	 */
1530
	public function getPerPage()
1531
	{
1532
		$per_page = $this->per_page ?: reset($this->items_per_page_list);
1533
1534
		if ($per_page !== 'all' && !in_array($this->per_page, $this->items_per_page_list)) {
1535
			$per_page = reset($this->items_per_page_list);
1536
		}
1537
1538
		return $per_page;
1539
	}
1540
1541
1542
	/**
1543
	 * Get associative array of items_per_page_list
1544
	 * @return array
1545
	 */
1546
	public function getItemsPerPageList()
1547
	{
1548
		$list = array_flip($this->items_per_page_list);
1549
1550
		foreach ($list as $key => $value) {
1551
			$list[$key] = $key;
1552
		}
1553
1554
		$list['all'] = $this->getTranslator()->translate('All');
1555
1556
		return $list;
1557
	}
1558
1559
1560
	/**
1561
	 * Order Grid to "be paginated"
1562
	 * @param bool $do
1563
	 * @return static
1564
	 */
1565
	public function setPagination($do)
1566
	{
1567
		$this->do_paginate = (bool) $do;
1568
1569
		return $this;
1570
	}
1571
1572
1573
	/**
1574
	 * Tell whether Grid is paginated
1575
	 * @return bool
1576
	 */
1577
	public function isPaginated()
1578
	{
1579
		return $this->do_paginate;
1580
	}
1581
1582
1583
	/**
1584
	 * Return current paginator class
1585
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1586
	 */
1587
	public function getPaginator()
1588
	{
1589
		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...
1590
			return $this['paginator'];
1591
		}
1592
1593
		return NULL;
1594
	}
1595
1596
1597
	/********************************************************************************
1598
	 *                                     I18N                                     *
1599
	 ********************************************************************************/
1600
1601
1602
	/**
1603
	 * Set datagrid translator
1604
	 * @param Nette\Localization\ITranslator $translator
1605
	 * @return static
1606
	 */
1607
	public function setTranslator(Nette\Localization\ITranslator $translator)
1608
	{
1609
		$this->translator = $translator;
1610
1611
		return $this;
1612
	}
1613
1614
1615
	/**
1616
	 * Get translator for datagrid
1617
	 * @return Nette\Localization\ITranslator
1618
	 */
1619
	public function getTranslator()
1620
	{
1621
		if (!$this->translator) {
1622
			$this->translator = new Localization\SimpleTranslator;
1623
		}
1624
1625
		return $this->translator;
1626
	}
1627
1628
1629
	/********************************************************************************
1630
	 *                                 COLUMNS ORDER                                *
1631
	 ********************************************************************************/
1632
1633
1634
	/**
1635
	 * Set order of datagrid columns
1636
	 * @param array $order
1637
	 * @return static
1638
	 */
1639
	public function setColumnsOrder($order)
1640
	{
1641
		$new_order = [];
1642
1643
		foreach ($order as $key) {
1644
			if (isset($this->columns[$key])) {
1645
				$new_order[$key] = $this->columns[$key];
1646
			}
1647
		}
1648
1649
		if (sizeof($new_order) === sizeof($this->columns)) {
1650
			$this->columns = $new_order;
1651
		} else {
1652
			throw new DataGridException('When changing columns order, you have to specify all columns');
1653
		}
1654
1655
		return $this;
1656
	}
1657
1658
1659
	/**
1660
	 * Columns order may be different for export and normal grid
1661
	 * @param array $order
1662
	 */
1663
	public function setColumnsExportOrder($order)
1664
	{
1665
		$this->columns_export_order = (array) $order;
1666
	}
1667
1668
1669
	/********************************************************************************
1670
	 *                                SESSION & URL                                 *
1671
	 ********************************************************************************/
1672
1673
1674
	/**
1675
	 * Find some unique session key name
1676
	 * @return string
1677
	 */
1678
	public function getSessionSectionName()
1679
	{
1680
		return $this->getPresenter()->getName().':'.$this->getName();
1681
	}
1682
1683
1684
	/**
1685
	 * Should datagrid remember its filters/pagination/etc using session?
1686
	 * @param bool $remember
1687
	 * @return static
1688
	 */
1689
	public function setRememberState($remember = TRUE)
1690
	{
1691
		$this->remember_state = (bool) $remember;
1692
1693
		return $this;
1694
	}
1695
1696
1697
	/**
1698
	 * Should datagrid refresh url using history API?
1699
	 * @param bool $refresh
1700
	 * @return static
1701
	 */
1702
	public function setRefreshUrl($refresh = TRUE)
1703
	{
1704
		$this->refresh_url = (bool) $refresh;
1705
1706
1707
		return $this;
1708
	}
1709
1710
1711
	/**
1712
	 * Get session data if functionality is enabled
1713
	 * @param  string $key
1714
	 * @return mixed
1715
	 */
1716
	public function getSessionData($key = NULL, $default_value = NULL)
1717
	{
1718
		if (!$this->remember_state) {
1719
			return $key ? $default_value : [];
1720
		}
1721
1722
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
1723
	}
1724
1725
1726
	/**
1727
	 * Save session data - just if it is enabled
1728
	 * @param  string $key
1729
	 * @param  mixed  $value
1730
	 * @return void
1731
	 */
1732
	public function saveSessionData($key, $value)
1733
	{
1734
		if ($this->remember_state) {
1735
			$this->grid_session->{$key} = $value;
1736
		}
1737
	}
1738
1739
1740
	/**
1741
	 * Delete session data
1742
	 * @return void
1743
	 */
1744
	public function deleteSesssionData($key)
1745
	{
1746
		unset($this->grid_session->{$key});
1747
	}
1748
1749
1750
	/********************************************************************************
1751
	 *                                  ITEM DETAIL                                 *
1752
	 ********************************************************************************/
1753
1754
1755
	/**
1756
	 * Get items detail parameters
1757
	 * @return array
1758
	 */
1759
	public function getItemsDetail()
1760
	{
1761
		return $this->items_detail;
1762
	}
1763
1764
1765
	/**
1766
	 * Items can have thair detail - toggled
1767
	 * @param mixed $detail callable|string|bool
1768
	 * @param bool|NULL $primary_where_column
1769
	 * @return Column\ItemDetail
1770
	 */
1771
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1772
	{
1773
		if ($this->isSortable()) {
1774
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1775
		}
1776
1777
		$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...
1778
			$this,
1779
			$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...
1780
		);
1781
1782
		if (is_string($detail)) {
1783
			/**
1784
			 * Item detail will be in separate template
1785
			 */
1786
			$this->items_detail->setType('template');
1787
			$this->items_detail->setTemplate($detail);
1788
1789
		} else if (is_callable($detail)) {
1790
			/**
1791
			 * Item detail will be rendered via custom callback renderer
1792
			 */
1793
			$this->items_detail->setType('renderer');
1794
			$this->items_detail->setRenderer($detail);
1795
1796
		} else if (TRUE === $detail) {
1797
			/**
1798
			 * Item detail will be rendered probably via block #detail
1799
			 */
1800
			$this->items_detail->setType('block');
1801
1802
		} else {
1803
			throw new DataGridException(
1804
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1805
			);
1806
		}
1807
1808
		return $this->items_detail;
1809
	}
1810
1811
1812
	/********************************************************************************
1813
	 *                                ROW PRIVILEGES                                *
1814
	 ********************************************************************************/
1815
1816
1817
	public function allowRowsGroupAction(callable $condition)
1818
	{
1819
		$this->row_conditions['group_action'] = $condition;
1820
	}
1821
1822
1823
	public function allowRowsAction($key, callable $condition)
1824
	{
1825
		$this->row_conditions['action'][$key] = $condition;
1826
	}
1827
1828
1829
	public function getRowCondition($name, $key = NULL)
1830
	{
1831
		if (!isset($this->row_conditions[$name])) {
1832
			return FALSE;
1833
		}
1834
1835
		$condition = $this->row_conditions[$name];
1836
1837
		if (!$key) {
1838
			return $condition;
1839
		}
1840
1841
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1842
	}
1843
1844
1845
	/********************************************************************************
1846
	 *                               HIDEABLE COLUMNS                               *
1847
	 ********************************************************************************/
1848
1849
1850
	/**
1851
	 * Can datagrid hide colums?
1852
	 * @return boolean
1853
	 */
1854
	public function canHideColumns()
1855
	{
1856
		return (bool) $this->can_hide_columns;
1857
	}
1858
1859
1860
	/**
1861
	 * Order Grid to set columns hideable.
1862
	 * @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...
1863
	 * @return static
1864
	 */
1865
	public function setColumnsHideable()
1866
	{
1867
		$this->can_hide_columns = TRUE;
1868
1869
		return $this;
1870
	}
1871
1872
1873
	/********************************************************************************
1874
	 *                                   INTERNAL                                   *
1875
	 ********************************************************************************/
1876
1877
1878
	/**
1879
	 * Get cont of columns
1880
	 * @return int
1881
	 */
1882
	public function getColumnsCount()
1883
	{
1884
		$count = sizeof($this->getColumns());
1885
1886
		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...
1887
			$count++;
1888
		}
1889
1890
		if ($this->hasGroupActions()) {
1891
			$count++;
1892
		}
1893
1894
		return $count;
1895
	}
1896
1897
1898
	/**
1899
	 * Get primary key of datagrid data source
1900
	 * @return string
1901
	 */
1902
	public function getPrimaryKey()
1903
	{
1904
		return $this->primary_key;
1905
	}
1906
1907
1908
	/**
1909
	 * Get set of set columns
1910
	 * @return Column\IColumn[]
1911
	 */
1912
	public function getColumns()
1913
	{
1914
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
1915
			$this->removeColumn($column);
1916
		}
1917
1918
		return $this->columns;
1919
	}
1920
1921
1922
1923
	/**
1924
	 * @return PresenterComponent
1925
	 */
1926
	public function getParent()
1927
	{
1928
		$parent = parent::getParent();
1929
		if (!$parent instanceof PresenterComponent) {
1930
			throw new DataGridHasToBeAttachedToPresenterComponentException(
1931
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
1932
			);
1933
		}
1934
1935
		return $parent;
1936
	}
1937
1938
}
1939
1940
1941
class DataGridException extends \Exception
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

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

Loading history...
1942
{
1943
}
1944