Completed
Push — master ( 56fa54...ba9513 )
by Petr
15:01
created

Grid::getItemsForCountSelect()   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

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 1
cts 1
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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
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
        $this->strictMode && trigger_error($message, E_USER_NOTICE);
976 1
    }
977
}
978