Completed
Pull Request — master (#122)
by
unknown
02:36
created

DataGrid::addColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2015 ublaboo <[email protected]>
5
 * @author      Pavel Janda <[email protected]>
6
 * @package     Ublaboo
7
 */
8
9
namespace Ublaboo\DataGrid;
10
11
use Nette;
12
use Nette\Application\UI\PresenterComponent;
13
use Ublaboo\DataGrid\Utils\ArraysHelper;
14
use Nette\Application\UI\Form;
15
use Ublaboo\DataGrid\Exception\DataGridException;
16
use Ublaboo\DataGrid\Exception\DataGridHasToBeAttachedToPresenterComponentException;
17
18
class DataGrid extends Nette\Application\UI\Control
19
{
20
21
	/**
22
	 * @var callable[]
23
	 */
24
	public $onRedraw;
25
26
	/**
27
	 * @var string
28
	 */
29
	public static $icon_prefix = 'fa fa-';
30
31
	/**
32
	 * When set to TRUE, datagrid throws an exception
33
	 * 	when tring to get related entity within join and entity does not exist
34
	 * @var bool
35
	 */
36
	public $strict_entity_property = FALSE;
37
38
	/**
39
	 * @var int
40
	 * @persistent
41
	 */
42
	public $page = 1;
43
44
	/**
45
	 * @var int
46
	 * @persistent
47
	 */
48
	public $per_page;
49
50
	/**
51
	 * @var array
52
	 * @persistent
53
	 */
54
	public $sort = [];
55
56
	/**
57
	 * @var array
58
	 */
59
	public $default_sort = [];
60
61
	/**
62
	 * @var array
63
	 * @persistent
64
	 */
65
	public $filter = [];
66
67
	/**
68
	 * @var Callable[]
69
	 */
70
	public $onRender = [];
71
72
	/**
73
	 * @var bool
74
	 */
75
	protected $use_happy_components = TRUE;
76
77
	/**
78
	 * @var Callable[]
79
	 */
80
	protected $rowCallback;
81
82
	/**
83
	 * @var array
84
	 */
85
	protected $items_per_page_list;
86
87
	/**
88
	 * @var string
89
	 */
90
	protected $template_file;
91
92
	/**
93
	 * @var Column\IColumn[]
94
	 */
95
	protected $columns = [];
96
97
	/**
98
	 * @var Column\Action[]
99
	 */
100
	protected $actions = [];
101
102
	/**
103
	 * @var GroupAction\GroupActionCollection
104
	 */
105
	protected $group_action_collection;
106
107
	/**
108
	 * @var Filter\Filter[]
109
	 */
110
	protected $filters = [];
111
112
	/**
113
	 * @var Export\Export[]
114
	 */
115
	protected $exports = [];
116
117
	/**
118
	 * @var DataModel
119
	 */
120
	protected $dataModel;
121
122
	/**
123
	 * @var DataFilter
124
	 */
125
	protected $dataFilter;
126
127
	/**
128
	 * @var string
129
	 */
130
	protected $primary_key = 'id';
131
132
	/**
133
	 * @var bool
134
	 */
135
	protected $do_paginate = TRUE;
136
137
	/**
138
	 * @var bool
139
	 */
140
	protected $csv_export = TRUE;
141
142
	/**
143
	 * @var bool
144
	 */
145
	protected $csv_export_filtered = TRUE;
146
147
	/**
148
	 * @var bool
149
	 */
150
	protected $sortable = FALSE;
151
152
	/**
153
	 * @var string
154
	 */
155
	protected $sortable_handler = 'sort!';
156
157
	/**
158
	 * @var string
159
	 */
160
	protected $original_template;
161
162
	/**
163
	 * @var array
164
	 */
165
	protected $redraw_item;
166
167
	/**
168
	 * @var mixed
169
	 */
170
	protected $translator;
171
172
	/**
173
	 * @var bool
174
	 */
175
	protected $force_filter_active;
176
177
	/**
178
	 * @var callable
179
	 */
180
	protected $tree_view_children_callback;
181
182
	/**
183
	 * @var string
184
	 */
185
	protected $tree_view_has_children_column;
186
187
	/**
188
	 * @var bool
189
	 */
190
	protected $outer_filter_rendering = FALSE;
191
192
	/**
193
	 * @var array
194
	 */
195
	protected $columns_export_order = [];
196
197
	/**
198
	 * @var bool
199
	 */
200
	private $remember_state = TRUE;
201
202
	/**
203
	 * @var bool
204
	 */
205
	private $refresh_url = TRUE;
206
207
	/**
208
	 * @var Nette\Http\SessionSection
209
	 */
210
	private $grid_session;
211
212
	/**
213
	 * @var Column\ItemDetail
214
	 */
215
	private $items_detail;
216
217
	/**
218
	 * @var array
219
	 */
220
	private $row_conditions = [
221
		'group_action' => FALSE,
222
		'action' => []
223
	];
224
225
	/**
226
	 * @var bool
227
	 */
228
	protected $can_hide_columns = FALSE;
229
230
	/** @var callable[] */
231
	public $onColumnAdd;
232
233
234
	/**
235
	 * @param Nette\ComponentModel\IContainer|NULL $parent
236
	 * @param string                               $name
237
	 */
238
	public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
239
	{
240
		parent::__construct($parent, $name);
241
242
		$this->monitor('Nette\Application\UI\Presenter');
243
	}
244
245
246
	/**
247
	 * {inheritDoc}
248
	 * @return void
249
	 */
250
	public function attached($presenter)
251
	{
252
		parent::attached($presenter);
253
254
		if ($presenter instanceof Nette\Application\UI\Presenter) {
255
			/**
256
			 * Get session
257
			 */
258
			$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...
259
260
			/**
261
			 * Try to find previous filters/pagination/sort in session
262
			 */
263
			$this->findSessionFilters();
264
			$this->findDefaultSort();
265
		}
266
	}
267
268
269
	/********************************************************************************
270
	 *                                  RENDERING                                   *
271
	 ********************************************************************************/
272
273
274
	/**
275
	 * Render template
276
	 * @return void
277
	 */
278
	public function render()
279
	{
280
		/**
281
		 * Check whether datagrid has set some columns, initiated data source, etc
282
		 */
283
		if (!($this->dataModel instanceof DataModel)) {
284
			throw new DataGridException('You have to set a data source first.');
285
		}
286
287
		if (empty($this->columns)) {
288
			throw new DataGridException('You have to add at least one column.');
289
		}
290
291
		$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...
292
293
		/**
294
		 * Invoke some possible events
295
		 */
296
		$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...
297
298
		/**
299
		 * Prepare data for rendering (datagrid may render just one item)
300
		 */
301
		$rows = [];
302
303
		if (!empty($this->redraw_item)) {
304
			$items = $this->dataModel->filterRow($this->redraw_item);
305
		} else {
306
			$items = Nette\Utils\Callback::invokeArgs(
307
				[$this->dataModel, 'filterData'],
308
				[
309
					$this->getPaginator(),
310
					$this->sort,
311
					$this->assableFilters()
312
				]
313
			);
314
		}
315
316
		$callback = $this->rowCallback ?: NULL;
317
318
		foreach ($items as $item) {
319
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
320
321
			if ($callback) {
322
				$callback($item, $row->getControl());
323
			}
324
		}
325
326
		if ($this->isTreeView()) {
327
			$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...
328
		}
329
330
		$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...
331
332
		$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...
333
		$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...
334
		$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...
335
		$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...
336
337
		$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...
338
		$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...
339
		$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...
340
		$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...
341
342
		/**
343
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
344
		 */
345
		$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...
346
347
		/**
348
		 * Set template file and render it
349
		 */
350
		$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...
351
	}
352
353
354
	/********************************************************************************
355
	 *                                 ROW CALLBACK                                 *
356
	 ********************************************************************************/
357
358
359
	/**
360
	 * Each row can be modified with user callback
361
	 * @param  callable  $callback
362
	 * @return static
363
	 */
364
	public function setRowCallback(callable $callback)
365
	{
366
		$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...
367
368
		return $this;
369
	}
370
371
372
	/********************************************************************************
373
	 *                                 DATA SOURCE                                  *
374
	 ********************************************************************************/
375
376
377
	/**
378
	 * By default ID, you can change that
379
	 * @param string $primary_key
380
	 * @return static
381
	 */
382
	public function setPrimaryKey($primary_key)
383
	{
384
		if ($this->dataModel instanceof DataModel) {
385
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
386
		}
387
388
		$this->primary_key = $primary_key;
389
390
		return $this;
391
	}
392
393
394
	/**
395
	 * Set Grid data source
396
	 * @param DataSource\IDataSource|array|\DibiFluent|Nette\Database\Table\Selection|\Kdyby\Doctrine\QueryBuilder $source
397
	 * @return static
398
	 */
399
	public function setDataSource($source)
400
	{
401
		$this->dataModel = new DataModel($source, $this->primary_key);
402
403
		return $this;
404
	}
405
406
407
	/********************************************************************************
408
	 *                                  TEMPLATING                                  *
409
	 ********************************************************************************/
410
411
412
	/**
413
	 * Set custom template file to render
414
	 * @param string $template_file
415
	 * @return static
416
	 */
417
	public function setTemplateFile($template_file)
418
	{
419
		$this->template_file = $template_file;
420
421
		return $this;
422
	}
423
424
425
	/**
426
	 * Get DataGrid template file
427
	 * @return string
428
	 * @return static
429
	 */
430
	public function getTemplateFile()
431
	{
432
		return $this->template_file ?: $this->getOriginalTemplateFile();
433
	}
434
435
436
	/**
437
	 * Get DataGrid original template file
438
	 * @return string
439
	 */
440
	public function getOriginalTemplateFile()
441
	{
442
		return __DIR__.'/templates/datagrid.latte';
443
	}
444
445
446
	/**
447
	 * Tell datagrid wheteher to use or not happy components
448
	 * @param  boolean|NULL $use If not given, return value of static::$use_happy_components
449
	 * @return void|bool
450
	 */
451
	public function useHappyComponents($use = NULL)
452
	{
453
		if (NULL === $use) {
454
			return $this->use_happy_components;
455
		}
456
457
		$this->use_happy_components = (bool) $use;
458
	}
459
460
461
	/********************************************************************************
462
	 *                                   SORTING                                    *
463
	 ********************************************************************************/
464
465
466
	/**
467
	 * Set default sorting
468
	 * @param array $sort
469
	 * @return static
470
	 */
471
	public function setDefaultSort($sort)
472
	{
473
		if (is_string($sort)) {
474
			$sort = [$sort => 'ASC'];
475
		} else {
476
			$sort = (array) $sort;
477
		}
478
479
		$this->default_sort = $sort;
480
481
		return $this;
482
	}
483
484
485
	/**
486
	 * User may set default sorting, apply it
487
	 * @return void
488
	 */
489
	public function findDefaultSort()
490
	{
491
		if (!empty($this->sort)) {
492
			return;
493
		}
494
495
		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...
496
			$this->sort = $this->default_sort;
497
		}
498
499
		$this->saveSessionData('_grid_sort', $this->sort);
500
	}
501
502
503
	/**
504
	 * Set grido to be sortable
505
	 * @param bool $sortable
506
	 * @return static
507
	 */
508
	public function setSortable($sortable = TRUE)
509
	{
510
		if ($this->getItemsDetail()) {
511
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
512
		}
513
514
		$this->sortable = (bool) $sortable;
515
516
		return $this;
517
	}
518
519
520
	/**
521
	 * Set sortable handle
522
	 * @param string $handler
523
	 * @return static
524
	 */
525
	public function setSortableHandler($handler = 'sort!')
526
	{
527
		$this->sortable_handler = (string) $handler;
528
529
		return $this;
530
	}
531
532
533
	/**
534
	 * Tell whether DataGrid is sortable
535
	 * @return bool
536
	 */
537
	public function isSortable()
538
	{
539
		return $this->sortable;
540
	}
541
542
	/**
543
	 * Return sortable handle name
544
	 * @return string
545
	 */
546
	public function getSortableHandler()
547
	{
548
		return $this->sortable_handler;
549
	}
550
551
552
	/********************************************************************************
553
	 *                                  TREE VIEW                                   *
554
	 ********************************************************************************/
555
556
557
	/**
558
	 * Is tree view set?
559
	 * @return boolean
560
	 */
561
	public function isTreeView()
562
	{
563
		return (bool) $this->tree_view_children_callback;
564
	}
565
566
567
	/**
568
	 * Setting tree view
569
	 * @param callable $get_children_callback
570
	 * @param string $tree_view_has_children_column
571
	 * @return static
572
	 */
573
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
574
	{
575
		if (!is_callable($get_children_callback)) {
576
			throw new DataGridException(
577
				'Parameters to method DataGrid::setTreeView must be of type callable'
578
			);
579
		}
580
581
		$this->tree_view_children_callback = $get_children_callback;
582
		$this->tree_view_has_children_column = $tree_view_has_children_column;
583
584
		/**
585
		 * TUrn off pagination
586
		 */
587
		$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...
588
589
		/**
590
		 * Set tree view template file
591
		 */
592
		if (!$this->template_file) {
593
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
594
		}
595
596
		return $this;
597
	}
598
599
600
	/********************************************************************************
601
	 *                                    COLUMNS                                   *
602
	 ********************************************************************************/
603
604
605
	/**
606
	 * Add text column with no other formating
607
	 * @param  string      $key
608
	 * @param  string      $name
609
	 * @param  string|null $column
610
	 * @return Column\ColumnText
611
	 */
612
	public function addColumnText($key, $name, $column = NULL)
613
	{
614
		$this->addColumnCheck($key);
615
		$column = $column ?: $key;
616
617
		return $this->addColumn($key, new Column\ColumnText($column, $name));
618
	}
619
620
621
	/**
622
	 * Add column with link
623
	 * @param  string      $key
624
	 * @param  string      $name
625
	 * @param  string|null $column
626
	 * @return Column\ColumnLink
627
	 */
628
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
629
	{
630
		$this->addColumnCheck($key);
631
		$column = $column ?: $key;
632
		$href = $href ?: $key;
633
634
		if (NULL === $params) {
635
			$params = [$this->primary_key];
636
		}
637
638
		return $this->addColumn($key, new Column\ColumnLink($this, $column, $name, $href, $params));
639
	}
640
641
642
	/**
643
	 * Add column with possible number formating
644
	 * @param  string      $key
645
	 * @param  string      $name
646
	 * @param  string|null $column
647
	 * @return Column\ColumnNumber
648
	 */
649
	public function addColumnNumber($key, $name, $column = NULL)
650
	{
651
		$this->addColumnCheck($key);
652
		$column = $column ?: $key;
653
654
		return $this->addColumn($key, new Column\ColumnNumber($column, $name));
655
	}
656
657
658
	/**
659
	 * Add column with date formating
660
	 * @param  string      $key
661
	 * @param  string      $name
662
	 * @param  string|null $column
663
	 * @return Column\ColumnDateTime
664
	 */
665
	public function addColumnDateTime($key, $name, $column = NULL)
666
	{
667
		$this->addColumnCheck($key);
668
		$column = $column ?: $key;
669
670
		return $this->addColumn($key, new Column\ColumnDateTime($column, $name));
671
	}
672
673
674
	/**
675
	 * @param string $key
676
	 * @param Column\Column $column
677
	 * @return Column\Column
678
	 */
679
	protected function addColumn($key, Column\Column $column)
680
	{
681
		$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...
682
		return $this->columns[$key] = $column;
683
	}
684
685
686
	/**
687
	 * Return existing column
688
	 * @param  string $key
689
	 * @return Column\Column
690
	 * @throws DataGridException
691
	 */
692
	public function getColumn($key)
693
	{
694
		if (!isset($this->columns[$key])) {
695
			throw new DataGridException("There is no column at key [$key] defined.");
696
		}
697
698
		return $this->columns[$key];
699
	}
700
701
702
	/**
703
	 * Remove column
704
	 * @param string $key
705
	 * @return void
706
	 */
707
	public function removeColumn($key)
708
	{
709
		unset($this->columns[$key]);
710
	}
711
712
713
	/**
714
	 * Check whether given key already exists in $this->columns
715
	 * @param  string $key
716
	 * @throws DataGridException
717
	 */
718
	protected function addColumnCheck($key)
719
	{
720
		if (isset($this->columns[$key])) {
721
			throw new DataGridException("There is already column at key [$key] defined.");
722
		}
723
	}
724
725
726
	/********************************************************************************
727
	 *                                    ACTIONS                                   *
728
	 ********************************************************************************/
729
730
731
	/**
732
	 * Create action
733
	 * @param string     $key
734
	 * @param string     $name
735
	 * @param string     $href
736
	 * @param array|null $params
737
	 * @return Column\Action
738
	 */
739
	public function addAction($key, $name, $href = NULL, array $params = NULL)
740
	{
741
		$this->addActionCheck($key);
742
		$href = $href ?: $key;
743
744
		if (NULL === $params) {
745
			$params = [$this->primary_key];
746
		}
747
748
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
749
	}
750
751
752
	/**
753
	 * Create action callback
754
	 * @param string     $key
755
	 * @param string     $name
756
	 * @return Column\Action
757
	 */
758
	public function addActionCallback($key, $name, $callback = NULL)
759
	{
760
		$this->addActionCheck($key);
761
		$params = ['__id' => $this->primary_key];
762
763
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
764
765
		if ($callback) {
766
			if (!is_callable($callback)) {
767
				throw new DataGridException('ActionCallback callback has to be callable.');
768
			}
769
770
			$action->onClick[] = $callback;
771
		}
772
773
		return $action;
774
	}
775
776
777
	/**
778
	 * Get existing action
779
	 * @param  string       $key
780
	 * @return Column\Action
781
	 * @throws DataGridException
782
	 */
783
	public function getAction($key)
784
	{
785
		if (!isset($this->actions[$key])) {
786
			throw new DataGridException("There is no action at key [$key] defined.");
787
		}
788
789
		return $this->actions[$key];
790
	}
791
792
793
	/**
794
	 * Remove action
795
	 * @param string $key
796
	 * @return void
797
	 */
798
	public function removeAction($key)
799
	{
800
		unset($this->actions[$key]);
801
	}
802
803
804
	/**
805
	 * Check whether given key already exists in $this->filters
806
	 * @param  string $key
807
	 * @throws DataGridException
808
	 */
809
	protected function addActionCheck($key)
810
	{
811
		if (isset($this->actions[$key])) {
812
			throw new DataGridException("There is already action at key [$key] defined.");
813
		}
814
	}
815
816
817
	/********************************************************************************
818
	 *                                    FILTERS                                   *
819
	 ********************************************************************************/
820
821
822
	/**
823
	 * Add filter fot text search
824
	 * @param string       $key
825
	 * @param string       $name
826
	 * @param array|string $columns
827
	 * @return Filter\FilterText
828
	 * @throws DataGridException
829
	 */
830
	public function addFilterText($key, $name, $columns = NULL)
831
	{
832
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
833
834
		if (!is_array($columns)) {
835
			throw new DataGridException("Filter Text can except only array or string.");
836
		}
837
838
		$this->addFilterCheck($key);
839
840
		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...
841
	}
842
843
844
	/**
845
	 * Add select box filter
846
	 * @param string $key
847
	 * @param string $name
848
	 * @param array  $options
849
	 * @param string $column
850
	 * @return Filter\FilterSelect
851
	 * @throws DataGridException
852
	 */
853 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...
854
	{
855
		$column = $column ?: $key;
856
857
		if (!is_string($column)) {
858
			throw new DataGridException("Filter Select can only filter through one column.");
859
		}
860
861
		$this->addFilterCheck($key);
862
863
		return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column);
864
	}
865
866
867
	/**
868
	 * Add datepicker filter
869
	 * @param string $key
870
	 * @param string $name
871
	 * @param string $column
872
	 * @return Filter\FilterDate
873
	 * @throws DataGridException
874
	 */
875
	public function addFilterDate($key, $name, $column = NULL)
876
	{
877
		$column = $column ?: $key;
878
879
		if (!is_string($column)) {
880
			throw new DataGridException("FilterDate can only filter through one column.");
881
		}
882
883
		$this->addFilterCheck($key);
884
885
		return $this->filters[$key] = new Filter\FilterDate($key, $name, $column);
886
	}
887
888
889
	/**
890
	 * Add range filter (from - to)
891
	 * @param string $key
892
	 * @param string $name
893
	 * @param string $column
894
	 * @return Filter\FilterRange
895
	 * @throws DataGridException
896
	 */
897 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...
898
	{
899
		$column = $column ?: $key;
900
901
		if (!is_string($column)) {
902
			throw new DataGridException("FilterRange can only filter through one column.");
903
		}
904
905
		$this->addFilterCheck($key);
906
907
		return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second);
908
	}
909
910
911
	/**
912
	 * Add datepicker filter (from - to)
913
	 * @param string $key
914
	 * @param string $name
915
	 * @param string $column
916
	 * @return Filter\FilterDateRange
917
	 * @throws DataGridException
918
	 */
919 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...
920
	{
921
		$column = $column ?: $key;
922
923
		if (!is_string($column)) {
924
			throw new DataGridException("FilterDateRange can only filter through one column.");
925
		}
926
927
		$this->addFilterCheck($key);
928
929
		return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second);
930
	}
931
932
933
	/**
934
	 * Check whether given key already exists in $this->filters
935
	 * @param  string $key
936
	 * @throws DataGridException
937
	 */
938
	protected function addFilterCheck($key)
939
	{
940
		if (isset($this->filters[$key])) {
941
			throw new DataGridException("There is already action at key [$key] defined.");
942
		}
943
	}
944
945
946
	/**
947
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
948
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
949
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
950
	 */
951
	public function assableFilters()
952
	{
953
		foreach ($this->filter as $key => $value) {
954
			if (!isset($this->filters[$key])) {
955
				$this->deleteSesssionData($key);
956
957
				continue;
958
			}
959
960
			if (is_array($value) || $value instanceof \Traversable) {
961
				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...
962
					$this->filters[$key]->setValue($value);
963
				}
964
			} else {
965
				if ($value !== '' && $value !== NULL) {
966
					$this->filters[$key]->setValue($value);
967
				}
968
			}
969
		}
970
971
		foreach ($this->columns as $column) {
972
			if (isset($this->sort[$column->getColumnName()])) {
973
				$column->setSort($this->sort);
974
			}
975
		}
976
977
		return $this->filters;
978
	}
979
980
981
	/**
982
	 * Remove filter
983
	 * @param string $key
984
	 * @return void
985
	 */
986
	public function removeFilter($key)
987
	{
988
		unset($this->filters[$key]);
989
	}
990
991
992
	/********************************************************************************
993
	 *                                  FILTERING                                   *
994
	 ********************************************************************************/
995
996
997
	/**
998
	 * Is filter active?
999
	 * @return boolean
1000
	 */
1001
	public function isFilterActive()
1002
	{
1003
		$is_filter = ArraysHelper::testTruthy($this->filter);
1004
1005
		return ($is_filter) || $this->force_filter_active;
1006
	}
1007
1008
1009
	/**
1010
	 * Tell that filter is active from whatever reasons
1011
	 * return static
1012
	 */
1013
	public function setFilterActive()
1014
	{
1015
		$this->force_filter_active = TRUE;
1016
1017
		return $this;
1018
	}
1019
1020
1021
	/**
1022
	 * If we want to sent some initial filter
1023
	 * @param array $filter
1024
	 * @return static
1025
	 */
1026
	public function setFilter(array $filter)
1027
	{
1028
		$this->filter = $filter;
1029
1030
		return $this;
1031
	}
1032
1033
1034
	/**
1035
	 * FilterAndGroupAction form factory
1036
	 * @return Form
1037
	 */
1038
	public function createComponentFilter()
1039
	{
1040
		$form = new Form($this, 'filter');
1041
1042
		$form->setTranslator($this->getTranslator());
1043
1044
		$form->setMethod('get');
1045
1046
		/**
1047
		 * Filter part
1048
		 */
1049
		$filter_container = $form->addContainer('filter');
1050
1051
		foreach ($this->filters as $filter) {
1052
			$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...
1053
		}
1054
1055
		/**
1056
		 * Group action part
1057
		 */
1058
		$group_action_container = $form->addContainer('group_action');
1059
1060
		if ($this->hasGroupActions()) {
1061
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1062
		}
1063
1064
		$form->setDefaults(['filter' => $this->filter]);
1065
1066
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1067
1068
		return $form;
1069
	}
1070
1071
1072
	/**
1073
	 * Set $this->filter values after filter form submitted
1074
	 * @param  Form $form
1075
	 * @return void
1076
	 */
1077
	public function filterSucceeded(Form $form)
1078
	{
1079
		$values = $form->getValues();
1080
1081
		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...
1082
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1083
				return;
1084
			}
1085
		}
1086
1087
		$values = $values['filter'];
1088
1089
		foreach ($values as $key => $value) {
1090
			/**
1091
			 * Session stuff
1092
			 */
1093
			$this->saveSessionData($key, $value);
1094
1095
			/**
1096
			 * Other stuff
1097
			 */
1098
			$this->filter[$key] = $value;
1099
		}
1100
1101
		$this->reload();
1102
	}
1103
1104
1105
	/**
1106
	 * Should be datagrid filters rendered separately?
1107
	 * @param boolean $out
1108
	 * @return static
1109
	 */
1110
	public function setOuterFilterRendering($out = TRUE)
1111
	{
1112
		$this->outer_filter_rendering = (bool) $out;
1113
1114
		return $this;
1115
	}
1116
1117
1118
	/**
1119
	 * Are datagrid filters rendered separately?
1120
	 * @return boolean
1121
	 */
1122
	public function hasOuterFilterRendering()
1123
	{
1124
		return $this->outer_filter_rendering;
1125
	}
1126
1127
1128
	/**
1129
	 * Try to restore session stuff
1130
	 * @return void
1131
	 */
1132
	public function findSessionFilters()
1133
	{
1134
		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...
1135
			return;
1136
		}
1137
1138
		if (!$this->remember_state) {
1139
			return;
1140
		}
1141
1142
		if ($page = $this->getSessionData('_grid_page')) {
1143
			$this->page = $page;
1144
		}
1145
1146
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1147
			$this->per_page = $per_page;
1148
		}
1149
1150
		if ($sort = $this->getSessionData('_grid_sort')) {
1151
			$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...
1152
		}
1153
1154
		foreach ($this->getSessionData() as $key => $value) {
1155
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page', '_grid_hidden_columns'])) {
1156
				$this->filter[$key] = $value;
1157
			}
1158
		}
1159
	}
1160
1161
1162
	/********************************************************************************
1163
	 *                                    EXPORTS                                   *
1164
	 ********************************************************************************/
1165
1166
1167
	/**
1168
	 * Add export of type callback
1169
	 * @param string $text
1170
	 * @param callable $callback
1171
	 * @param boolean $filtered
1172
	 * @return Export\Export
1173
	 */
1174
	public function addExportCallback($text, $callback, $filtered = FALSE)
1175
	{
1176
		if (!is_callable($callback)) {
1177
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1178
		}
1179
1180
		return $this->addToExports(new Export\Export($text, $callback, $filtered));
1181
	}
1182
1183
1184
	/**
1185
	 * Add already implemented csv export
1186
	 * @param string $text
1187
	 * @param string $csv_file_name
1188
	 * @return Export\Export
1189
	 */
1190
	public function addExportCsv($text, $csv_file_name)
1191
	{
1192
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE));
1193
	}
1194
1195
1196
	/**
1197
	 * Add already implemented csv export, but for filtered data
1198
	 * @param string $text
1199
	 * @param string $csv_file_name
1200
	 * @return Export\Export
1201
	 */
1202
	public function addExportCsvFiltered($text, $csv_file_name)
1203
	{
1204
		return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE));
1205
	}
1206
1207
1208
	/**
1209
	 * Add export to array
1210
	 * @param Export\Export $export
1211
	 * @return Export\Export
1212
	 */
1213
	protected function addToExports(Export\Export $export)
1214
	{
1215
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1216
1217
		$export->setLink($this->link('export!', ['id' => $id]));
1218
1219
		return $this->exports[$id] = $export;
1220
	}
1221
1222
1223
	public function resetExportsLinks()
1224
	{
1225
		foreach ($this->exports as $id => $export) {
1226
			$export->setLink($this->link('export!', ['id' => $id]));
1227
		}
1228
	}
1229
1230
1231
	/********************************************************************************
1232
	 *                                 GROUP ACTIONS                                *
1233
	 ********************************************************************************/
1234
1235
1236
	/**
1237
	 * Add group actino
1238
	 * @param string $title
1239
	 * @param array  $options
1240
	 * @return GroupAction\GroupAction
1241
	 */
1242
	public function addGroupAction($title, $options = [])
1243
	{
1244
		return $this->getGroupActionCollection()->addGroupAction($title, $options);
1245
	}
1246
1247
1248
	/**
1249
	 * Get collection of all group actions
1250
	 * @return GroupAction\GroupActionCollection
1251
	 */
1252
	public function getGroupActionCollection()
1253
	{
1254
		if (!$this->group_action_collection) {
1255
			$this->group_action_collection = new GroupAction\GroupActionCollection();
1256
		}
1257
1258
		return $this->group_action_collection;
1259
	}
1260
1261
1262
	/**
1263
	 * Has datagrid some group actions?
1264
	 * @return boolean
1265
	 */
1266
	public function hasGroupActions()
1267
	{
1268
		return (bool) $this->group_action_collection;
1269
	}
1270
1271
1272
	/********************************************************************************
1273
	 *                                   HANDLERS                                   *
1274
	 ********************************************************************************/
1275
1276
1277
	/**
1278
	 * Handler for changind page (just refresh site with page as persistent paramter set)
1279
	 * @param  int  $page
1280
	 * @return void
1281
	 */
1282
	public function handlePage($page)
1283
	{
1284
		/**
1285
		 * Session stuff
1286
		 */
1287
		$this->page = $page;
1288
		$this->saveSessionData('_grid_page', $page);
1289
1290
		$this->reload(['table']);
1291
	}
1292
1293
1294
	/**
1295
	 * Handler for sorting
1296
	 * @param array $sort
1297
	 * @return void
1298
	 */
1299
	public function handleSort(array $sort)
1300
	{
1301
		/**
1302
		 * Session stuff
1303
		 */
1304
		$this->sort = $sort;
1305
		$this->saveSessionData('_grid_sort', $this->sort);
1306
1307
		$this->reload(['table']);
1308
	}
1309
1310
1311
	/**
1312
	 * handler for reseting the filter
1313
	 * @return void
1314
	 */
1315
	public function handleResetFilter()
1316
	{
1317
		/**
1318
		 * Session stuff
1319
		 */
1320
		$this->deleteSesssionData('_grid_page');
1321
1322
		foreach ($this->getSessionData() as $key => $value) {
1323
			if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) {
1324
				$this->deleteSesssionData($key);
1325
			}
1326
		}
1327
1328
		$this->filter = [];
1329
1330
		$this->reload(['grid']);
1331
	}
1332
1333
1334
	/**
1335
	 * Handler for export
1336
	 * @param  int $id Key for particular export class in array $this->exports
1337
	 * @return void
1338
	 */
1339
	public function handleExport($id)
1340
	{
1341
		if (!isset($this->exports[$id])) {
1342
			throw new Nette\Application\ForbiddenRequestException;
1343
		}
1344
1345
		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...
1346
			$this->setColumnsOrder($this->columns_export_order);
1347
		}
1348
1349
		$export = $this->exports[$id];
1350
1351
		if ($export->isFiltered()) {
1352
			$sort      = $this->sort;
1353
			$filter    = $this->assableFilters();
1354
		} else {
1355
			$sort      = $this->primary_key;
1356
			$filter    = [];
1357
		}
1358
1359
		if (NULL === $this->dataModel) {
1360
			throw new DataGridException('You have to set a data source first.');
1361
		}
1362
1363
		$rows = [];
1364
1365
		$items = Nette\Utils\Callback::invokeArgs(
1366
			[$this->dataModel, 'filterData'], [NULL, $sort, $filter]
1367
		);
1368
1369
		foreach ($items as $item) {
1370
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
1371
		}
1372
1373
		if ($export instanceof Export\ExportCsv) {
1374
			$export->invoke($rows, $this);
1375
		} else {
1376
			$export->invoke($items, $this);
1377
		}
1378
1379
		if ($export->isAjax()) {
1380
			$this->reload();
1381
		}
1382
	}
1383
1384
1385
	/**
1386
	 * Handler for getting children of parent item (e.g. category)
1387
	 * @param  int $parent
1388
	 * @return void
1389
	 */
1390
	public function handleGetChildren($parent)
1391
	{
1392
		$this->setDataSource(
1393
			call_user_func($this->tree_view_children_callback, $parent)
1394
		);
1395
1396
		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...
1397
			$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...
1398
			$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...
1399
1400
			$this->redrawControl('items');
1401
1402
			$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...
1403
		} else {
1404
			$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...
1405
		}
1406
	}
1407
1408
1409
	/**
1410
	 * Handler for getting item detail
1411
	 * @param  mixed $id
1412
	 * @return void
1413
	 */
1414
	public function handleGetItemDetail($id)
1415
	{
1416
		$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...
1417
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
1418
1419
		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...
1420
			$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...
1421
			$this->redrawControl('items');
1422
1423
			$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...
1424
		} else {
1425
			$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...
1426
		}
1427
	}
1428
1429
1430
	/**
1431
	 * Handler for inline editing
1432
	 * @param  mixed $id
1433
	 * @param  mixed $key
1434
	 * @return void
1435
	 */
1436
	public function handleEdit($id, $key)
1437
	{
1438
		$column = $this->getColumn($key);
1439
		$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...
1440
1441
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
1442
	}
1443
1444
1445
	/**
1446
	 * Redraw $this
1447
	 * @return void
1448
	 */
1449
	public function reload($snippets = [])
1450
	{
1451
		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...
1452
			$this->redrawControl('tbody');
1453
			$this->redrawControl('pagination');
1454
1455
			/**
1456
			 * manualy reset exports links...
1457
			 */
1458
			$this->resetExportsLinks();
1459
			$this->redrawControl('exports');
1460
1461
			foreach ($snippets as $snippet) {
1462
				$this->redrawControl($snippet);
1463
			}
1464
1465
			$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...
1466
1467
			$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...
1468
		} else {
1469
			$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...
1470
		}
1471
	}
1472
1473
1474
	/**
1475
	 * Redraw just one row via ajax
1476
	 * @param  int   $id
1477
	 * @param  mixed $primary_where_column
1478
	 * @return void
1479
	 */
1480
	public function redrawItem($id, $primary_where_column = NULL)
1481
	{
1482
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
1483
1484
		$this->redrawControl('items');
1485
		$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...
1486
1487
		$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...
1488
	}
1489
1490
1491
	/**
1492
	 * Tell datagrid to display all columns
1493
	 * @return void
1494
	 */
1495
	public function handleShowAllColumns()
1496
	{
1497
		$this->deleteSesssionData('_grid_hidden_columns');
1498
1499
		$this->redrawControl();
1500
1501
		$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...
1502
	}
1503
1504
1505
	/**
1506
	 * Notice datagrid to not display particular columns
1507
	 * @param  string $column
1508
	 * @return void
1509
	 */
1510
	public function handleHideColumn($column)
1511
	{
1512
		/**
1513
		 * Store info about hiding a column to session
1514
		 */
1515
		$columns = $this->getSessionData('_grid_hidden_columns');
1516
1517
		if (empty($columns)) {
1518
			$columns = [$column];
1519
		} else if (!in_array($column, $columns)) {
1520
			array_push($columns, $column);
1521
		}
1522
1523
		$this->saveSessionData('_grid_hidden_columns', $columns);
1524
1525
		$this->redrawControl();
1526
1527
		$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...
1528
	}
1529
1530
1531
	public function handleActionCallback($__key, $__id)
1532
	{
1533
		$action = $this->getAction($__key);
1534
1535
		if (!($action instanceof Column\ActionCallback)) {
1536
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
1537
		}
1538
1539
		$action->onClick($__id);
1540
	}
1541
1542
1543
	/********************************************************************************
1544
	 *                                  PAGINATION                                  *
1545
	 ********************************************************************************/
1546
1547
1548
	/**
1549
	 * Set options of select "items_per_page"
1550
	 * @param array $items_per_page_list
1551
	 * @return static
1552
	 */
1553
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
1554
	{
1555
		$this->items_per_page_list = $items_per_page_list;
1556
1557
		if ($include_all) {
1558
			$this->items_per_page_list[] = 'all';
1559
		}
1560
1561
		return $this;
1562
	}
1563
1564
1565
	/**
1566
	 * Paginator factory
1567
	 * @return Components\DataGridPaginator\DataGridPaginator
1568
	 */
1569
	public function createComponentPaginator()
1570
	{
1571
		/**
1572
		 * Init paginator
1573
		 */
1574
		$component = new Components\DataGridPaginator\DataGridPaginator(
1575
			$this->getTranslator()
1576
		);
1577
		$paginator = $component->getPaginator();
1578
1579
		$paginator->setPage($this->page);
1580
		$paginator->setItemsPerPage($this->getPerPage());
1581
1582
		return $component;
1583
	}
1584
1585
1586
	/**
1587
	 * PerPage form factory
1588
	 * @return Form
1589
	 */
1590
	public function createComponentPerPage()
1591
	{
1592
		$form = new Form;
1593
1594
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1595
			->setValue($this->getPerPage());
1596
1597
		$form->addSubmit('submit', '');
1598
1599
		$saveSessionData = [$this, 'saveSessionData'];
1600
1601
		$form->onSuccess[] = function($form, $values) use ($saveSessionData) {
1602
			/**
1603
			 * Session stuff
1604
			 */
1605
			$saveSessionData('_grid_per_page', $values->per_page);
1606
1607
			/**
1608
			 * Other stuff
1609
			 */
1610
			$this->per_page = $values->per_page;
1611
			$this->reload();
1612
		};
1613
1614
		return $form;
1615
	}
1616
1617
1618
	/**
1619
	 * Get parameter per_page
1620
	 * @return int
1621
	 */
1622
	public function getPerPage()
1623
	{
1624
		$items_per_page_list = $this->getItemsPerPageList();
1625
1626
		$per_page = $this->per_page ?: reset($items_per_page_list);
1627
1628
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
1629
			$per_page = reset($items_per_page_list);
1630
		}
1631
1632
		return $per_page;
1633
	}
1634
1635
1636
	/**
1637
	 * Get associative array of items_per_page_list
1638
	 * @return array
1639
	 */
1640
	public function getItemsPerPageList()
1641
	{
1642
		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...
1643
			$this->setItemsPerPageList([10, 20, 50], TRUE);
1644
		}
1645
1646
		$list = array_flip($this->items_per_page_list);
1647
1648
		foreach ($list as $key => $value) {
1649
			$list[$key] = $key;
1650
		}
1651
1652
		if (array_key_exists('all', $list)) {
1653
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
1654
		}
1655
1656
		return $list;
1657
	}
1658
1659
1660
	/**
1661
	 * Order Grid to "be paginated"
1662
	 * @param bool $do
1663
	 * @return static
1664
	 */
1665
	public function setPagination($do)
1666
	{
1667
		$this->do_paginate = (bool) $do;
1668
1669
		return $this;
1670
	}
1671
1672
1673
	/**
1674
	 * Tell whether Grid is paginated
1675
	 * @return bool
1676
	 */
1677
	public function isPaginated()
1678
	{
1679
		return $this->do_paginate;
1680
	}
1681
1682
1683
	/**
1684
	 * Return current paginator class
1685
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
1686
	 */
1687
	public function getPaginator()
1688
	{
1689
		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...
1690
			return $this['paginator'];
1691
		}
1692
1693
		return NULL;
1694
	}
1695
1696
1697
	/********************************************************************************
1698
	 *                                     I18N                                     *
1699
	 ********************************************************************************/
1700
1701
1702
	/**
1703
	 * Set datagrid translator
1704
	 * @param Nette\Localization\ITranslator $translator
1705
	 * @return static
1706
	 */
1707
	public function setTranslator(Nette\Localization\ITranslator $translator)
1708
	{
1709
		$this->translator = $translator;
1710
1711
		return $this;
1712
	}
1713
1714
1715
	/**
1716
	 * Get translator for datagrid
1717
	 * @return Nette\Localization\ITranslator
1718
	 */
1719
	public function getTranslator()
1720
	{
1721
		if (!$this->translator) {
1722
			$this->translator = new Localization\SimpleTranslator;
1723
		}
1724
1725
		return $this->translator;
1726
	}
1727
1728
1729
	/********************************************************************************
1730
	 *                                 COLUMNS ORDER                                *
1731
	 ********************************************************************************/
1732
1733
1734
	/**
1735
	 * Set order of datagrid columns
1736
	 * @param array $order
1737
	 * @return static
1738
	 */
1739
	public function setColumnsOrder($order)
1740
	{
1741
		$new_order = [];
1742
1743
		foreach ($order as $key) {
1744
			if (isset($this->columns[$key])) {
1745
				$new_order[$key] = $this->columns[$key];
1746
			}
1747
		}
1748
1749
		if (sizeof($new_order) === sizeof($this->columns)) {
1750
			$this->columns = $new_order;
1751
		} else {
1752
			throw new DataGridException('When changing columns order, you have to specify all columns');
1753
		}
1754
1755
		return $this;
1756
	}
1757
1758
1759
	/**
1760
	 * Columns order may be different for export and normal grid
1761
	 * @param array $order
1762
	 */
1763
	public function setColumnsExportOrder($order)
1764
	{
1765
		$this->columns_export_order = (array) $order;
1766
	}
1767
1768
1769
	/********************************************************************************
1770
	 *                                SESSION & URL                                 *
1771
	 ********************************************************************************/
1772
1773
1774
	/**
1775
	 * Find some unique session key name
1776
	 * @return string
1777
	 */
1778
	public function getSessionSectionName()
1779
	{
1780
		return $this->getPresenter()->getName().':'.$this->getName();
1781
	}
1782
1783
1784
	/**
1785
	 * Should datagrid remember its filters/pagination/etc using session?
1786
	 * @param bool $remember
1787
	 * @return static
1788
	 */
1789
	public function setRememberState($remember = TRUE)
1790
	{
1791
		$this->remember_state = (bool) $remember;
1792
1793
		return $this;
1794
	}
1795
1796
1797
	/**
1798
	 * Should datagrid refresh url using history API?
1799
	 * @param bool $refresh
1800
	 * @return static
1801
	 */
1802
	public function setRefreshUrl($refresh = TRUE)
1803
	{
1804
		$this->refresh_url = (bool) $refresh;
1805
1806
1807
		return $this;
1808
	}
1809
1810
1811
	/**
1812
	 * Get session data if functionality is enabled
1813
	 * @param  string $key
1814
	 * @return mixed
1815
	 */
1816
	public function getSessionData($key = NULL, $default_value = NULL)
1817
	{
1818
		if (!$this->remember_state) {
1819
			return $key ? $default_value : [];
1820
		}
1821
1822
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
1823
	}
1824
1825
1826
	/**
1827
	 * Save session data - just if it is enabled
1828
	 * @param  string $key
1829
	 * @param  mixed  $value
1830
	 * @return void
1831
	 */
1832
	public function saveSessionData($key, $value)
1833
	{
1834
		if ($this->remember_state) {
1835
			$this->grid_session->{$key} = $value;
1836
		}
1837
	}
1838
1839
1840
	/**
1841
	 * Delete session data
1842
	 * @return void
1843
	 */
1844
	public function deleteSesssionData($key)
1845
	{
1846
		unset($this->grid_session->{$key});
1847
	}
1848
1849
1850
	/********************************************************************************
1851
	 *                                  ITEM DETAIL                                 *
1852
	 ********************************************************************************/
1853
1854
1855
	/**
1856
	 * Get items detail parameters
1857
	 * @return array
1858
	 */
1859
	public function getItemsDetail()
1860
	{
1861
		return $this->items_detail;
1862
	}
1863
1864
1865
	/**
1866
	 * Items can have thair detail - toggled
1867
	 * @param mixed $detail callable|string|bool
1868
	 * @param bool|NULL $primary_where_column
1869
	 * @return Column\ItemDetail
1870
	 */
1871
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
1872
	{
1873
		if ($this->isSortable()) {
1874
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
1875
		}
1876
1877
		$this->items_detail = new Column\ItemDetail(
1878
			$this,
1879
			$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...
1880
		);
1881
1882
		if (is_string($detail)) {
1883
			/**
1884
			 * Item detail will be in separate template
1885
			 */
1886
			$this->items_detail->setType('template');
1887
			$this->items_detail->setTemplate($detail);
1888
1889
		} else if (is_callable($detail)) {
1890
			/**
1891
			 * Item detail will be rendered via custom callback renderer
1892
			 */
1893
			$this->items_detail->setType('renderer');
1894
			$this->items_detail->setRenderer($detail);
1895
1896
		} else if (TRUE === $detail) {
1897
			/**
1898
			 * Item detail will be rendered probably via block #detail
1899
			 */
1900
			$this->items_detail->setType('block');
1901
1902
		} else {
1903
			throw new DataGridException(
1904
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
1905
			);
1906
		}
1907
1908
		return $this->items_detail;
1909
	}
1910
1911
1912
	/********************************************************************************
1913
	 *                                ROW PRIVILEGES                                *
1914
	 ********************************************************************************/
1915
1916
1917
	public function allowRowsGroupAction(callable $condition)
1918
	{
1919
		$this->row_conditions['group_action'] = $condition;
1920
	}
1921
1922
1923
	public function allowRowsAction($key, callable $condition)
1924
	{
1925
		$this->row_conditions['action'][$key] = $condition;
1926
	}
1927
1928
1929
	public function getRowCondition($name, $key = NULL)
1930
	{
1931
		if (!isset($this->row_conditions[$name])) {
1932
			return FALSE;
1933
		}
1934
1935
		$condition = $this->row_conditions[$name];
1936
1937
		if (!$key) {
1938
			return $condition;
1939
		}
1940
1941
		return isset($condition[$key]) ? $condition[$key] : FALSE;
1942
	}
1943
1944
1945
	/********************************************************************************
1946
	 *                               HIDEABLE COLUMNS                               *
1947
	 ********************************************************************************/
1948
1949
1950
	/**
1951
	 * Can datagrid hide colums?
1952
	 * @return boolean
1953
	 */
1954
	public function canHideColumns()
1955
	{
1956
		return (bool) $this->can_hide_columns;
1957
	}
1958
1959
1960
	/**
1961
	 * Order Grid to set columns hideable.
1962
	 * @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...
1963
	 * @return static
1964
	 */
1965
	public function setColumnsHideable()
1966
	{
1967
		$this->can_hide_columns = TRUE;
1968
1969
		return $this;
1970
	}
1971
1972
1973
	/********************************************************************************
1974
	 *                                   INTERNAL                                   *
1975
	 ********************************************************************************/
1976
1977
1978
	/**
1979
	 * Get cont of columns
1980
	 * @return int
1981
	 */
1982
	public function getColumnsCount()
1983
	{
1984
		$count = sizeof($this->getColumns());
1985
1986
		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...
1987
			$count++;
1988
		}
1989
1990
		if ($this->hasGroupActions()) {
1991
			$count++;
1992
		}
1993
1994
		return $count;
1995
	}
1996
1997
1998
	/**
1999
	 * Get primary key of datagrid data source
2000
	 * @return string
2001
	 */
2002
	public function getPrimaryKey()
2003
	{
2004
		return $this->primary_key;
2005
	}
2006
2007
2008
	/**
2009
	 * Get set of set columns
2010
	 * @return Column\IColumn[]
2011
	 */
2012
	public function getColumns()
2013
	{
2014
		foreach ($this->getSessionData('_grid_hidden_columns', []) as $column) {
2015
			$this->removeColumn($column);
2016
		}
2017
2018
		return $this->columns;
2019
	}
2020
2021
2022
	/**
2023
	 * @return PresenterComponent
2024
	 */
2025
	public function getParent()
2026
	{
2027
		$parent = parent::getParent();
2028
2029
		if (!($parent instanceof PresenterComponent)) {
2030
			throw new DataGridHasToBeAttachedToPresenterComponentException(
2031
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
2032
			);
2033
		}
2034
2035
		return $parent;
2036
	}
2037
2038
}
2039