Completed
Push — master ( 083ae7...f26a73 )
by Petr
06:36
created

Grid::handlePage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.125

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 1
cts 2
cp 0.5
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1.125
1
<?php
2
3
/**
4
 * This file is part of the Grido (http://grido.bugyik.cz)
5
 *
6
 * Copyright (c) 2011 Petr Bugyík (http://petr.bugyik.cz)
7
 *
8
 * For the full copyright and license information, please view
9
 * the file LICENSE.md that was distributed with this source code.
10
 */
11
12
namespace Grido;
13
14
use Grido\Exception;
15
use Grido\Components\Paginator;
16
use Grido\Components\Columns\Column;
17
use Grido\Components\Filters\Filter;
18
19
use Symfony\Component\PropertyAccess\PropertyAccessor;
20
21
/**
22
 * Grido - DataGrid for Nette Framework.
23
 *
24
 * @package     Grido
25
 * @author      Petr Bugyík
26
 *
27
 * @property-read int $count
28
 * @property-read mixed $data
29 1
 * @property-read \Nette\Utils\Html $tablePrototype
30
 * @property-read PropertyAccessor $propertyAccessor
31
 * @property-write string $templateFile
32
 * @property bool $rememberState
33
 * @property array $defaultPerPage
34
 * @property array $defaultFilter
35
 * @property array $defaultSort
36
 * @property array $perPageList
37
 * @property \Nette\Localization\ITranslator $translator
38
 * @property Paginator $paginator
39
 * @property string $primaryKey
40 1
 * @property string $filterRenderType
41
 * @property DataSources\IDataSource $model
42 1
 * @property callback $rowCallback
43
 * @property bool $strictMode
44 1
 * @method void onRegistered(Grid $grid)
45
 * @method void onRender(Grid $grid)
46 1
 * @method void onFetchData(Grid $grid)
47
 */
48
class Grid extends Components\Container
49 1
{
50 1
    /***** DEFAULTS ****/
51
    const BUTTONS = 'buttons';
52
53
    const CLIENT_SIDE_OPTIONS = 'grido-options';
54
55
    /** @var int @persistent */
56
    public $page = 1;
57
58
    /** @var int @persistent */
59
    public $perPage;
60
61
    /** @var array @persistent */
62
    public $sort = [];
63
64
    /** @var array @persistent */
65
    public $filter = [];
66 1
67
    /** @var array event on all grid's components registered */
68
    public $onRegistered;
69
70
    /** @var array event on render */
71
    public $onRender;
72
73
    /** @var array event for modifying data */
74
    public $onFetchData;
75
76
    /** @var callback returns tr html element; function($row, Html $tr) */
77
    protected $rowCallback;
78 1
79
    /** @var \Nette\Utils\Html */
80
    protected $tablePrototype;
81
82
    /** @var bool */
83
    protected $rememberState = FALSE;
84
85
    /** @var string */
86
    protected $rememberStateSectionName;
87
88
    /** @var string */
89
    protected $primaryKey = 'id';
90
91
    /** @var string */
92
    protected $filterRenderType;
93
94
    /** @var array */
95
    protected $perPageList = [10, 20, 30, 50, 100];
96
97
    /** @var int */
98
    protected $defaultPerPage = 20;
99
100
    /** @var array */
101
    protected $defaultFilter = [];
102
103
    /** @var array */
104
    protected $defaultSort = [];
105
106
    /** @var DataSources\IDataSource */
107
    protected $model;
108
109
    /** @var int total count of items */
110
    protected $count;
111
112
    /** @var mixed */
113
    protected $data;
114
115
    /** @var Paginator */
116
    protected $paginator;
117
118
    /** @var \Nette\Localization\ITranslator */
119
    protected $translator;
120
121
    /** @var PropertyAccessor */
122
    protected $propertyAccessor;
123
124
    /** @var bool */
125
    protected $strictMode = TRUE;
126
127 1
    /** @var array */
128
    protected $options = [
129
        self::CLIENT_SIDE_OPTIONS => []
130
    ];
131
132
    /**
133
     * Sets a model that implements the interface Grido\DataSources\IDataSource or data-source object.
134
     * @param mixed $model
135
     * @param bool $forceWrapper
136
     * @throws Exception
137
     * @return Grid
138
     */
139
    public function setModel($model, $forceWrapper = FALSE)
140
    {
141 1
        $this->model = $model instanceof DataSources\IDataSource && $forceWrapper === FALSE
1 ignored issue
show
Documentation Bug introduced by
It seems like $model instanceof \Grido...taSources\Model($model) can also be of type object<Grido\DataSources\Model>. However, the property $model is declared as type object<Grido\DataSources\IDataSource>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
142 1
            ? $model
143 1
            : new DataSources\Model($model);
144
145 1
        return $this;
146
    }
147
148
    /**
149
     * Sets the default number of items per page.
150
     * @param int $perPage
151
     * @return Grid
152
     */
153
    public function setDefaultPerPage($perPage)
154
    {
155 1
        $perPage = (int) $perPage;
156 1
        $this->defaultPerPage = $perPage;
157
158 1
        if (!in_array($perPage, $this->perPageList)) {
159 1
            $this->perPageList[] = $perPage;
160 1
            sort($this->perPageList);
161 1
        }
162
163 1
        return $this;
164
    }
165
166
    /**
167
     * Sets default filtering.
168
     * @param array $filter
169
     * @return Grid
170
     */
171
    public function setDefaultFilter(array $filter)
172
    {
173 1
        $this->defaultFilter = array_merge($this->defaultFilter, $filter);
174 1
        return $this;
175
    }
176
177
    /**
178
     * Sets default sorting.
179
     * @param array $sort
180
     * @return Grid
181
     * @throws Exception
182
     */
183
    public function setDefaultSort(array $sort)
184
    {
185 1
        static $replace = ['asc' => Column::ORDER_ASC, 'desc' => Column::ORDER_DESC];
186
187 1
        foreach ($sort as $column => $dir) {
188 1
            $dir = strtr(strtolower($dir), $replace);
189 1
            if (!in_array($dir, $replace)) {
190 1
                throw new Exception("Dir '$dir' for column '$column' is not allowed.");
191 1
            }
192
193 1
            $this->defaultSort[$column] = $dir;
194 1
        }
195
196 1
        return $this;
197
    }
198
199
    /**
200
     * Sets items to per-page select.
201
     * @param array $perPageList
202
     * @return Grid
203
     */
204
    public function setPerPageList(array $perPageList)
205
    {
206 1
        $this->perPageList = $perPageList;
207
208 1
        if ($this->hasFilters(FALSE) || $this->hasOperation(FALSE)) {
209 1
            $this['form']['count']->setItems($this->getItemsForCountSelect());
210 1
        }
211
212 1
        return $this;
213
    }
214
215
    /**
216
     * Sets translator.
217
     * @param \Nette\Localization\ITranslator $translator
218
     * @return Grid
219
     */
220
    public function setTranslator(\Nette\Localization\ITranslator $translator)
221
    {
222 1
        $this->translator = $translator;
223 1
        return $this;
224
    }
225
226
    /**
227
     * Sets type of filter rendering.
228
     * Defaults inner (Filter::RENDER_INNER) if column does not exist then outer filter (Filter::RENDER_OUTER).
229
     * @param string $type
230
     * @throws Exception
231
     * @return Grid
232
     */
233
    public function setFilterRenderType($type)
234
    {
235 1
        $type = strtolower($type);
236 1
        if (!in_array($type, [Filter::RENDER_INNER, Filter::RENDER_OUTER])) {
237 1
            throw new Exception('Type must be Filter::RENDER_INNER or Filter::RENDER_OUTER.');
238
        }
239
240 1
        $this->filterRenderType = $type;
241 1
        return $this;
242
    }
243
244
    /**
245
     * Sets custom paginator.
246
     * @param Paginator $paginator
247
     * @return Grid
248
     */
249
    public function setPaginator(Paginator $paginator)
250
    {
251 1
        $this->paginator = $paginator;
252 1
        return $this;
253
    }
254
255
    /**
256
     * Sets grid primary key.
257
     * Defaults is "id".
258
     * @param string $key
259
     * @return Grid
260
     */
261
    public function setPrimaryKey($key)
262
    {
263 1
        $this->primaryKey = $key;
264 1
        return $this;
265
    }
266
267
    /**
268
     * Sets file name of custom template.
269
     * @param string $file
270
     * @return Grid
271
     */
272
    public function setTemplateFile($file)
273
    {
274
        $this->onRender[] = function() use ($file) {
275
            $this->template->gridoTemplate = $this->getTemplate()->getFile();
0 ignored issues
show
Bug introduced by
Accessing gridoTemplate on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
276
            $this->getTemplate()->setFile($file);
277
        };
278
279
        return $this;
280
    }
281
282
    /**
283
     * Sets saving state to session.
284
     * @param bool $state
285
     * @param string $sectionName
286
     * @return Grid
287
     */
288
    public function setRememberState($state = TRUE, $sectionName = NULL)
289
    {
290 1
        $this->getPresenter(); //component must be attached to presenter
291 1
        $this->getRememberSession(TRUE); //start session if not
292 1
        $this->rememberState = (bool) $state;
293 1
        $this->rememberStateSectionName = $sectionName;
294
295 1
        return $this;
296
    }
297
298
    /**
299
     * Sets callback for customizing tr html object.
300
     * Callback returns tr html element; function($row, Html $tr).
301
     * @param $callback
302
     * @return Grid
303
     */
304
    public function setRowCallback($callback)
305
    {
306 1
        $this->rowCallback = $callback;
307 1
        return $this;
308
    }
309
310
    /**
311
     * Sets client-side options.
312
     * @param array $options
313
     * @return Grid
314
     */
315
    public function setClientSideOptions(array $options)
316
    {
317 1
        $this->options[self::CLIENT_SIDE_OPTIONS] = $options;
318 1
        return $this;
319
    }
320
321
    /**
322
     * Determines whether any user error will cause a notice.
323
     * @param bool $mode
324
     * @return \Grido\Grid
325
     */
326
    public function setStrictMode($mode)
327
    {
328 1
        $this->strictMode = (bool) $mode;
329 1
        return $this;
330
    }
331
332
    /**********************************************************************************************/
333
334
    /**
335
     * Returns total count of data.
336
     * @return int
337
     */
338
    public function getCount()
339
    {
340 1
        if ($this->count === NULL) {
341 1
            $this->count = $this->getModel()->getCount();
342 1
        }
343
344 1
        return $this->count;
345
    }
346
347
    /**
348
     * Returns default per page.
349
     * @return int
350
     */
351
    public function getDefaultPerPage()
352
    {
353 1
        if (!in_array($this->defaultPerPage, $this->perPageList)) {
354 1
            $this->defaultPerPage = $this->perPageList[0];
355 1
        }
356
357 1
        return $this->defaultPerPage;
358
    }
359
360
    /**
361
     * Returns default filter.
362
     * @return array
363
     */
364
    public function getDefaultFilter()
365
    {
366 1
        return $this->defaultFilter;
367
    }
368
369
    /**
370
     * Returns default sort.
371
     * @return array
372
     */
373
    public function getDefaultSort()
374
    {
375 1
        return $this->defaultSort;
376
    }
377
378
    /**
379
     * Returns list of possible items per page.
380
     * @return array
381
     */
382
    public function getPerPageList()
383
    {
384 1
        return $this->perPageList;
385
    }
386
387
    /**
388
     * Returns primary key.
389
     * @return string
390
     */
391
    public function getPrimaryKey()
392
    {
393 1
        return $this->primaryKey;
394
    }
395
396 1
    /**
397
     * Returns remember state.
398
     * @return bool
399
     */
400
    public function getRememberState()
401
    {
402 1
        return $this->rememberState;
403
    }
404
405
    /**
406
     * Returns row callback.
407
     * @return callback
408
     */
409
    public function getRowCallback()
410
    {
411 1
        return $this->rowCallback;
412
    }
413
414
    /**
415
     * Returns items per page.
416
     * @return int
417
     */
418
    public function getPerPage()
419
    {
420 1
        return $this->perPage === NULL
421 1
            ? $this->getDefaultPerPage()
422 1
            : $this->perPage;
423
    }
424
425
    /**
426
     * Returns actual filter values.
427
     * @param string $key
428
     * @return mixed
429
     */
430
    public function getActualFilter($key = NULL)
431
    {
432 1
        $filter = $this->filter ? $this->filter : $this->defaultFilter;
433 1
        return $key !== NULL && isset($filter[$key]) ? $filter[$key] : $filter;
434 1
    }
435
436
    /**
437
     * Returns fetched data.
438
     * @param bool $applyPaging
439
     * @param bool $useCache
440
     * @param bool $fetch
441
     * @throws Exception
442
     * @return array|DataSources\IDataSource|\Nette\Database\Table\Selection
443
     */
444
    public function getData($applyPaging = TRUE, $useCache = TRUE, $fetch = TRUE)
445
    {
446 1
        if ($this->getModel() === NULL) {
447 1
            throw new Exception('Model cannot be empty, please use method $grid->setModel().');
448
        }
449
450 1
        $data = $this->data;
451 1
        if ($data === NULL || $useCache === FALSE) {
452 1
            $this->applyFiltering();
453 1
            $this->applySorting();
454
455 1
            if ($applyPaging) {
456 1
                $this->applyPaging();
457 1
            }
458
459 1
            if ($fetch === FALSE) {
460 1
                return $this->getModel();
461
            }
462
463 1
            $data = $this->getModel()->getData();
464
465 1
            if ($useCache === TRUE) {
466 1
                $this->data = $data;
467 1
            }
468
469 1
            if ($applyPaging && !empty($data) && !in_array($this->page, range(1, $this->getPaginator()->pageCount))) {
470 1
                $this->__triggerUserNotice("Page is out of range.");
471 1
                $this->page = 1;
472 1
            }
473
474 1
            if (!empty($this->onFetchData)) {
475
                $this->onFetchData($this);
476
            }
477 1
        }
478
479 1
        return $data;
480
    }
481
482
    /**
483
     * Returns translator.
484
     * @return Translations\FileTranslator
485
     */
486
    public function getTranslator()
487
    {
488 1
        if ($this->translator === NULL) {
489 1
            $this->setTranslator(new Translations\FileTranslator);
490 1
        }
491
492 1
        return $this->translator;
493
    }
494
495
    /**
496
     * Returns remember session for set expiration, etc.
497
     * @param bool $forceStart - if TRUE, session will be started if not
498
     * @return \Nette\Http\SessionSection|NULL
499
     */
500
    public function getRememberSession($forceStart = FALSE)
501
    {
502 1
        $presenter = $this->getPresenter();
503 1
        $session = $presenter->getSession();
504
505 1
        if (!$session->isStarted() && $forceStart) {
506 1
            $session->start();
507 1
        }
508
509 1
        return $session->isStarted()
510 1
            ? ($session->getSection($this->rememberStateSectionName ?: ($presenter->name . ':' . $this->getUniqueId())))
0 ignored issues
show
Bug introduced by
Accessing name 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...
511 1
            : NULL;
512
    }
513
514
    /**
515
     * Returns table html element of grid.
516
     * @return \Nette\Utils\Html
517
     */
518
    public function getTablePrototype()
519
    {
520 1
        if ($this->tablePrototype === NULL) {
521 1
            $this->tablePrototype = \Nette\Utils\Html::el('table');
522 1
            $this->tablePrototype->id($this->getName())
523 1
                ->class[] = 'table table-striped table-hover';
524 1
        }
525
526 1
        return $this->tablePrototype;
527
    }
528
529
    /**
530
     * @return string
531
     * @internal
532
     */
533
    public function getFilterRenderType()
534
    {
535 1
        if ($this->filterRenderType !== NULL) {
536 1
            return $this->filterRenderType;
537
        }
538
539 1
        $this->filterRenderType = Filter::RENDER_OUTER;
540 1
        if ($this->hasColumns() && $this->hasFilters() && $this->hasActions()) {
541 1
            $this->filterRenderType = Filter::RENDER_INNER;
542
543 1
            $filters = $this[Filter::ID]->getComponents();
544 1
            foreach ($filters as $filter) {
545 1
                if (!$this[Column::ID]->getComponent($filter->name, FALSE)) {
546 1
                    $this->filterRenderType = Filter::RENDER_OUTER;
547 1
                    break;
548
                }
549 1
            }
550 1
        }
551
552 1
        return $this->filterRenderType;
553
    }
554
555
    /**
556
     * @return DataSources\IDataSource
557
     */
558
    public function getModel()
559
    {
560 1
        return $this->model;
561
    }
562
563
    /**
564
     * @return Paginator
565
     * @internal
566
     */
567
    public function getPaginator()
568
    {
569 1
        if ($this->paginator === NULL) {
570 1
            $this->paginator = new Paginator;
571 1
            $this->paginator->setItemsPerPage($this->getPerPage())
572 1
                ->setGrid($this);
573 1
        }
574
575 1
        return $this->paginator;
576
    }
577
578
    /**
579
     * A simple wrapper around symfony/property-access with Nette Database dot notation support.
580
     * @param array|object $object
581
     * @param string $name
582
     * @return mixed
583
     * @internal
584
     */
585
    public function getProperty($object, $name)
586
    {
587 1
        if ($object instanceof \Nette\Database\Table\IRow && \Nette\Utils\Strings::contains($name, '.')) {
588 1
            $parts = explode('.', $name);
589 1
            foreach ($parts as $item) {
590 1
                if (is_object($object)) {
591 1
                    $object = $object->$item;
592 1
                }
593 1
            }
594
595 1
            return $object;
596
        }
597
598 1
        if (is_array($object)) {
599 1
            $name = "[$name]";
600 1
        }
601
602 1
        return $this->getPropertyAccessor()->getValue($object, $name);
603
    }
604
605
    /**
606
     * @return PropertyAccessor
607
     * @internal
608
     */
609
    public function getPropertyAccessor()
610
    {
611 1
        if ($this->propertyAccessor === NULL) {
612 1
            $this->propertyAccessor = new PropertyAccessor(TRUE, TRUE);
613 1
        }
614
615 1
        return $this->propertyAccessor;
616
    }
617
618
    /**
619
     * @param mixed $row item from db
620
     * @return \Nette\Utils\Html
621
     * @internal
622
     */
623
    public function getRowPrototype($row)
624
    {
625
        try {
626 1
            $primaryValue = $this->getProperty($row, $this->getPrimaryKey());
627 1
        } catch (\Exception $e) {
628 1
            $primaryValue = NULL;
629
        }
630
631 1
        $tr = \Nette\Utils\Html::el('tr');
632 1
        $primaryValue ? $tr->class[] = "grid-row-$primaryValue" : NULL;
633
634 1
        if ($this->rowCallback) {
635 1
            $tr = call_user_func_array($this->rowCallback, [$row, $tr]);
636 1
        }
637
638 1
        return $tr;
639
    }
640
641
    /**
642
     * Returns client-side options.
643
     * @return array
644
     */
645
    public function getClientSideOptions()
646
    {
647 1
        return (array) $this->options[self::CLIENT_SIDE_OPTIONS];
648
    }
649
650
    /**
651
     * @return bool
652
     */
653
    public function isStrictMode()
654
    {
655 1
        return $this->strictMode;
656
    }
657
658
    /**********************************************************************************************/
659
660
    /**
661
     * Loads state informations.
662
     * @param array $params
663
     * @internal
664
     */
665
    public function loadState(array $params)
666
    {
667
        //loads state from session
668 1
        $session = $this->getRememberSession();
669 1
        if ($session && $this->getPresenter()->isSignalReceiver($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 isSignalReceiver() does only exist in the following implementations of said interface: KdybyModule\CliPresenter, 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...
670 1
            $session->remove();
671 1
        } elseif ($session && empty($params) && $session->params) {
672 1
            $params = (array) $session->params;
673 1
        }
674
675 1
        parent::loadState($params);
676 1
    }
677
678
    /**
679
     * Saves state informations for next request.
680
     * @param array $params
681
     * @param \Nette\Application\UI\PresenterComponentReflection $reflection (internal, used by Presenter)
682
     * @internal
683
     */
684
    public function saveState(array &$params, $reflection = NULL)
685
    {
686 1
        !empty($this->onRegistered) && $this->onRegistered($this);
687 1
        return parent::saveState($params, $reflection);
688
    }
689
690
    /**
691
     * Ajax method.
692
     * @internal
693
     */
694
    public function handleRefresh()
695
    {
696
        $this->reload();
697
    }
698
699
    /**
700
     * @param int $page
701
     * @internal
702
     */
703
    public function handlePage($page)
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
704
    {
705 1
        $this->reload();
706
    }
707
708
    /**
709
     * @param array $sort
710
     * @internal
711
     */
712
    public function handleSort(array $sort)
0 ignored issues
show
Unused Code introduced by
The parameter $sort is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
713
    {
714 1
        $this->page = 1;
715 1
        $this->reload();
716
    }
717
718
    /**
719
     * @param \Nette\Forms\Controls\SubmitButton $button
720
     * @internal
721
     */
722
    public function handleFilter(\Nette\Forms\Controls\SubmitButton $button)
723
    {
724 1
        $values = $button->form->values[Filter::ID];
725 1
        $session = $this->rememberState //session filter
726 1
            ? isset($this->getRememberSession(TRUE)->params['filter'])
727 1
                ? $this->getRememberSession(TRUE)->params['filter']
728 1
                : []
729 1
            : [];
730
731 1
        foreach ($values as $name => $value) {
732 1
            if (is_numeric($value) || !empty($value) || isset($this->defaultFilter[$name]) || isset($session[$name])) {
733 1
                $this->filter[$name] = $this->getFilter($name)->changeValue($value);
734 1
            } elseif (isset($this->filter[$name])) {
735
                unset($this->filter[$name]);
736
            }
737 1
        }
738
739 1
        $this->page = 1;
740 1
        $this->reload();
741
    }
742
743
    /**
744
     * @param \Nette\Forms\Controls\SubmitButton $button
745
     * @internal
746
     */
747
    public function handleReset(\Nette\Forms\Controls\SubmitButton $button)
748
    {
749 1
        $this->sort = [];
750 1
        $this->filter = [];
751 1
        $this->perPage = NULL;
752
753 1
        if ($session = $this->getRememberSession()) {
754 1
            $session->remove();
755 1
        }
756
757 1
        $button->form->setValues([Filter::ID => $this->defaultFilter], TRUE);
758
759 1
        $this->page = 1;
760 1
        $this->reload();
761
    }
762
763
    /**
764
     * @param \Nette\Forms\Controls\SubmitButton $button
765
     * @internal
766
     */
767
    public function handlePerPage(\Nette\Forms\Controls\SubmitButton $button)
768
    {
769 1
        $perPage = (int) $button->form['count']->value;
770 1
        $this->perPage = $perPage == $this->defaultPerPage
771 1
            ? NULL
772
            : $perPage;
773
774 1
        $this->page = 1;
775 1
        $this->reload();
776
    }
777
778
    /**
779
     * Refresh wrapper.
780
     * @return void
781
     * @internal
782
     */
783
    public function reload()
784
    {
785 1
        if ($this->presenter->isAjax()) {
786
            $this->presenter->payload->grido = TRUE;
787
            $this->redrawControl();
788
        } else {
789 1
            $this->redirect('this');
790
        }
791
    }
792
793
    /**********************************************************************************************/
794
795
    /**
796
     * @return \Nette\Templating\FileTemplate
797
     * @internal
798
     */
799
    public function createTemplate()
800
    {
801 1
        $template = parent::createTemplate();
802 1
        $template->setFile(__DIR__ . '/Grid.latte');
803 1
        $template->registerHelper('translate', [$this->getTranslator(), 'translate']);
804
805 1
        return $template;
806
    }
807
808
    /**
809
     * @internal
810
     * @throws Exception
811
     */
812
    public function render()
813
    {
814 1
        if (!$this->hasColumns()) {
815
            throw new Exception('Grid must have defined a column, please use method $grid->addColumn*().');
816
        }
817
818 1
        $this->saveRememberState();
819 1
        $data = $this->getData();
820
821 1
        if (!empty($this->onRender)) {
822 1
            $this->onRender($this);
823 1
        }
824
825 1
        $this->template->data = $data;
826 1
        $this->template->form = $form = $this['form'];
827 1
        $this->template->paginator = $this->getPaginator();
0 ignored issues
show
Bug introduced by
Accessing paginator on the interface Nette\Application\UI\ITemplate suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
828
829 1
        $form['count']->setValue($this->getPerPage());
830
831 1
        if ($options = $this->options[self::CLIENT_SIDE_OPTIONS]) {
832 1
            $this->getTablePrototype()->data[self::CLIENT_SIDE_OPTIONS] = json_encode($options);
833 1
        }
834
835 1
        $this->template->render();
836 1
    }
837
838
    protected function saveRememberState()
839
    {
840 1
        if ($this->rememberState) {
841 1
            $session = $this->getRememberSession(TRUE);
842 1
            $params = array_keys($this->getReflection()->getPersistentParams());
843 1
            foreach ($params as $param) {
844 1
                $session->params[$param] = $this->$param;
845 1
            }
846 1
        }
847 1
    }
848
849
    protected function applyFiltering()
850
    {
851 1
        $conditions = $this->__getConditions($this->getActualFilter());
852 1
        $this->getModel()->filter($conditions);
853 1
    }
854
855
    /**
856
     * @param array $filter
857
     * @return array
858
     * @internal
859
     */
860
    public function __getConditions(array $filter)
861
    {
862 1
        $conditions = [];
863 1
        if (!empty($filter)) {
864
            try {
865 1
                $this['form']->setDefaults([Filter::ID => $filter]);
866 1
            } catch (\Nette\InvalidArgumentException $e) {
867
                $this->__triggerUserNotice($e->getMessage());
868
                $filter = [];
869
                if ($session = $this->getRememberSession()) {
870
                    $session->remove();
871
                }
872
            }
873
874 1
            foreach ($filter as $column => $value) {
875 1
                if ($component = $this->getFilter($column, FALSE)) {
876 1
                    if ($condition = $component->__getCondition($value)) {
877 1
                        $conditions[] = $condition;
878 1
                    }
879 1
                } else {
880 1
                    $this->__triggerUserNotice("Filter with name '$column' does not exist.");
881
                }
882 1
            }
883 1
        }
884
885 1
        return $conditions;
886
    }
887
888
    protected function applySorting()
889
    {
890 1
        $sort = [];
891 1
        $this->sort = $this->sort ? $this->sort : $this->defaultSort;
892
893 1
        foreach ($this->sort as $column => $dir) {
894 1
            $component = $this->getColumn($column, FALSE);
895 1
            if (!$component) {
896 1
                if (!isset($this->defaultSort[$column])) {
897 1
                    $this->__triggerUserNotice("Column with name '$column' does not exist.");
898 1
                    break;
899
                }
900
901 1
            } elseif (!$component->isSortable()) {
902 1
                if (isset($this->defaultSort[$column])) {
903 1
                    $component->setSortable();
904 1
                } else {
905 1
                    $this->__triggerUserNotice("Column with name '$column' is not sortable.");
906 1
                    break;
907
                }
908 1
            }
909
910 1
            if (!in_array($dir, [Column::ORDER_ASC, Column::ORDER_DESC])) {
911 1
                if ($dir == '' && isset($this->defaultSort[$column])) {
912
                    unset($this->sort[$column]);
913
                    break;
914
                }
915
916 1
                $this->__triggerUserNotice("Dir '$dir' is not allowed.");
917 1
                break;
918
            }
919
920 1
            $sort[$component ? $component->column : $column] = $dir == Column::ORDER_ASC ? 'ASC' : 'DESC';
0 ignored issues
show
Documentation introduced by
The property $column is declared protected in Grido\Components\Columns\Column. 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...
921 1
        }
922
923 1
        if (!empty($sort)) {
924 1
            $this->getModel()->sort($sort);
925 1
        }
926 1
    }
927
928
    protected function applyPaging()
929
    {
930 1
        $paginator = $this->getPaginator()
931 1
            ->setItemCount($this->getCount())
932 1
            ->setPage($this->page);
933
934 1
        $perPage = $this->getPerPage();
935 1
        if ($perPage !== NULL && !in_array($perPage, $this->perPageList)) {
936 1
            $this->__triggerUserNotice("The number '$perPage' of items per page is out of range.");
937 1
        }
938
939 1
        $this->getModel()->limit($paginator->getOffset(), $paginator->getLength());
940 1
    }
941
942
    protected function createComponentForm($name)
943
    {
944 1
        $form = new \Nette\Application\UI\Form($this, $name);
945 1
        $form->setTranslator($this->getTranslator());
946 1
        $form->setMethod($form::GET);
947
948 1
        $buttons = $form->addContainer(self::BUTTONS);
949 1
        $buttons->addSubmit('search', 'Grido.Search')
950 1
            ->onClick[] = [$this, 'handleFilter'];
951 1
        $buttons->addSubmit('reset', 'Grido.Reset')
952 1
            ->onClick[] = [$this, 'handleReset'];
953 1
        $buttons->addSubmit('perPage', 'Grido.ItemsPerPage')
954 1
            ->onClick[] = [$this, 'handlePerPage'];
955
956 1
        $form->addSelect('count', 'Count', $this->getItemsForCountSelect())
957 1
            ->setTranslator(NULL)
958 1
            ->controlPrototype->attrs['title'] = $this->getTranslator()->translate('Grido.ItemsPerPage');
959 1
    }
960
961
    /**
962
     * @return array
963
     */
964
    protected function getItemsForCountSelect()
965
    {
966 1
        return array_combine($this->perPageList, $this->perPageList);
967
    }
968
969
    /**
970
     * @internal
971
     * @param string $message
972
     */
973
    public function __triggerUserNotice($message)
974
    {
975 1
        if ($this->getPresenter(FALSE) && $session = $this->getRememberSession()) {
976 1
            $session->remove();
977 1
        }
978
979 1
        $this->strictMode && trigger_error($message, E_USER_NOTICE);
980 1
    }
981
}
982