Completed
Pull Request — master (#120)
by
unknown
02:51
created

DataGrid::handleHideColumn()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1398
			$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...
1399
			$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...
1400
1401
			$this->redrawControl('items');
1402
1403
			$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...
1404
		} else {
1405
			$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...
1406
		}
1407
	}
1408
1409
1410
	/**
1411
	 * Handler for getting item detail
1412
	 * @param  mixed $id
1413
	 * @return void
1414
	 */
1415
	public function handleGetItemDetail($id)
1416
	{
1417
		$this->template->add('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...
1418
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1419
1420
		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...
1421
			$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...
1422
			$this->redrawControl('items');
1423
1424
			$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...
1425
		} else {
1426
			$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...
1427
		}
1428
	}
1429
1430
1431
	/**
1432
	 * Handler for inline editing
1433
	 * @param  mixed $id
1434
	 * @param  mixed $key
1435
	 * @return void
1436
	 */
1437
	public function handleEdit($id, $key)
1438
	{
1439
		$column = $this->getColumn($key);
1440
		$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...
1441
1442
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1443
	}
1444
1445
1446
	/**
1447
	 * Redraw $this
1448
	 * @return void
1449
	 */
1450
	public function reload($snippets = [])
1451
	{
1452
		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...
1453
			$this->redrawControl('tbody');
1454
			$this->redrawControl('pagination');
1455
1456
			/**
1457
			 * manualy reset exports links...
1458
			 */
1459
			$this->resetExportsLinks();
1460
			$this->redrawControl('exports');
1461
1462
			foreach ($snippets as $snippet) {
1463
				$this->redrawControl($snippet);
1464
			}
1465
1466
			$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...
1467
1468
			$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...
1469
		} else {
1470
			$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...
1471
		}
1472
	}
1473
1474
1475
	/**
1476
	 * Redraw just one row via ajax
1477
	 * @param  int   $id
1478
	 * @param  mixed $primary_where_column
1479
	 * @return void
1480
	 */
1481
	public function redrawItem($id, $primary_where_column = NULL)
1482
	{
1483
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1484
1485
		$this->redrawControl('items');
1486
		$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...
1487
1488
		$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...
1489
	}
1490
1491
1492
	/**
1493
	 * Tell datagrid to display all columns
1494
	 * @return void
1495
	 */
1496
	public function handleShowAllColumns()
1497
	{
1498
		$this->deleteSesssionData('_grid_hidden_columns');
1499
1500
		$this->redrawControl();
1501
1502
		$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...
1503
	}
1504
1505
1506
	/**
1507
	 * Notice datagrid to not display particular columns
1508
	 * @param  string $column
1509
	 * @return void
1510
	 */
1511
	public function handleHideColumn($column)
1512
	{
1513
		/**
1514
		 * Store info about hiding a column to session
1515
		 */
1516
		$columns = $this->getSessionData('_grid_hidden_columns');
1517
1518
		if (empty($columns)) {
1519
			$columns = [$column];
1520
		} else if (!in_array($column, $columns)) {
1521
			array_push($columns, $column);
1522
		}
1523
1524
		$this->saveSessionData('_grid_hidden_columns', $columns);
1525
1526
		$this->redrawControl();
1527
1528
		$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...
1529
	}
1530
1531
1532
	public function handleActionCallback($__key, $__id)
1533
	{
1534
		$action = $this->getAction($__key);
1535
1536
		if (!($action instanceof Column\ActionCallback)) {
1537
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1538
		}
1539
1540
		$action->onClick($__id);
1541
	}
1542
1543
1544
	/********************************************************************************
1545
	 *                                  PAGINATION                                  *
1546
	 ********************************************************************************/
1547
1548
1549
	/**
1550
	 * Set options of select "items_per_page"
1551
	 * @param array $items_per_page_list
1552
	 * @return static
1553
	 */
1554
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1555
	{
1556
		$this->items_per_page_list = $items_per_page_list;
1557
1558
		if ($include_all) {
1559
			$this->items_per_page_list[] = 'all';
1560
		}
1561
1562
		return $this;
1563
	}
1564
1565
1566
	/**
1567
	 * Paginator factory
1568
	 * @return Components\DataGridPaginator\DataGridPaginator
1569
	 */
1570
	public function createComponentPaginator()
1571
	{
1572
		/**
1573
		 * Init paginator
1574
		 */
1575
		$component = new Components\DataGridPaginator\DataGridPaginator(
1576
			$this->getTranslator()
1577
		);
1578
		$paginator = $component->getPaginator();
1579
1580
		$paginator->setPage($this->page);
1581
		$paginator->setItemsPerPage($this->getPerPage());
1582
1583
		return $component;
1584
	}
1585
1586
1587
	/**
1588
	 * PerPage form factory
1589
	 * @return Form
1590
	 */
1591
	public function createComponentPerPage()
1592
	{
1593
		$form = new Form;
1594
1595
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1596
			->setValue($this->getPerPage());
1597
1598
		$form->addSubmit('submit', '');
1599
1600
		$saveSessionData = [$this, 'saveSessionData'];
1601
1602
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1603
			/**
1604
			 * Session stuff
1605
			 */
1606
			$saveSessionData('_grid_per_page', $values->per_page);
1607
1608
			/**
1609
			 * Other stuff
1610
			 */
1611
			$this->per_page = $values->per_page;
1612
			$this->reload();
1613
		};
1614
1615
		return $form;
1616
	}
1617
1618
1619
	/**
1620
	 * Get parameter per_page
1621
	 * @return int
1622
	 */
1623
	public function getPerPage()
1624
	{
1625
		$items_per_page_list = $this->getItemsPerPageList();
1626
1627
		$per_page = $this->per_page ?: reset($items_per_page_list);
1628
1629
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
1630
			$per_page = reset($items_per_page_list);
1631
		}
1632
1633
		return $per_page;
1634
	}
1635
1636
1637
	/**
1638
	 * Get associative array of items_per_page_list
1639
	 * @return array
1640
	 */
1641
	public function getItemsPerPageList()
1642
	{
1643
		if (!$this->items_per_page_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->items_per_page_list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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