Completed
Pull Request — master (#299)
by
unknown
18:21
created

Grid::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 1
cp 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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\Button;
16
use Grido\Components\Paginator;
17
use Grido\Components\Columns\Column;
18
use Grido\Components\Filters\Filter;
19
use Grido\Components\Actions\Action;
20
21
use Symfony\Component\PropertyAccess\PropertyAccessor;
22
23
/**
24
 * Grido - DataGrid for Nette Framework.
25
 *
26
 * @package     Grido
27
 * @author      Petr Bugyík
28
 *
29
 * @property-read int $count
30
 * @property-read mixed $data
31
 * @property-read \Nette\Utils\Html $tablePrototype
32
 * @property-read PropertyAccessor $propertyAccessor
33
 * @property-read Customization $customization
34
 * @property-write string $templateFile
35
 * @property bool $rememberState
36
 * @property array $defaultPerPage
37
 * @property array $defaultFilter
38
 * @property array $defaultSort
39
 * @property array $perPageList
40
 * @property \Nette\Localization\ITranslator $translator
41
 * @property Paginator $paginator
42
 * @property string $primaryKey
43
 * @property string $filterRenderType
44
 * @property DataSources\IDataSource $model
45
 * @property callback $rowCallback
46
 * @property bool $strictMode
47
 * @method void onRegistered(Grid $grid)
48
 * @method void onRender(Grid $grid)
49
 * @method void onFetchData(Grid $grid)
50
 */
51
class Grid extends Components\Container
52 1
{
53
    /***** DEFAULTS ****/
54
    const BUTTONS = 'buttons';
55
56
    const CLIENT_SIDE_OPTIONS = 'grido-options';
57
58
    /** @var int @persistent */
59
    public $page = 1;
60
61
    /** @var int @persistent */
62
    public $perPage;
63
64
    /** @var array @persistent */
65
    public $sort = [];
66
67
    /** @var array @persistent */
68
    public $filter = [];
69
70
    /** @var array event on all grid's components registered */
71
    public $onRegistered;
72
73
    /** @var array event on render */
74
    public $onRender;
75
76
    /** @var array event for modifying data */
77
    public $onFetchData;
78
79
    /** @var callback returns tr html element; function($row, Html $tr) */
80
    protected $rowCallback;
81
82
    /** @var \Nette\Utils\Html */
83
    protected $tablePrototype;
84
85
    /** @var bool */
86
    protected $rememberState = FALSE;
87
88
    /** @var string */
89
    protected $rememberStateSectionName;
90
91
    /** @var string */
92
    protected $primaryKey = 'id';
93
94
    /** @var string */
95
    protected $filterRenderType;
96
97
    /** @var array */
98
    protected $perPageList = [10, 20, 30, 50, 100];
99
100
    /** @var int */
101
    protected $defaultPerPage = 20;
102
103
    /** @var array */
104
    protected $defaultFilter = [];
105
106
    /** @var array */
107
    protected $defaultSort = [];
108
109
    /** @var DataSources\IDataSource */
110
    protected $model;
111
112
    /** @var int total count of items */
113
    protected $count;
114
115
    /** @var mixed */
116
    protected $data;
117
118
    /** @var Paginator */
119
    protected $paginator;
120
121
    /** @var \Nette\Localization\ITranslator */
122
    protected $translator;
123
124
    /** @var PropertyAccessor */
125
    protected $propertyAccessor;
126
127
    /** @var bool */
128
    protected $strictMode = TRUE;
129
130
    /** @var array */
131
    protected $options = [
132
        self::CLIENT_SIDE_OPTIONS => []
133
    ];
134
135
    /** @var Customization */
136
    protected $customization;
137
138
    /**
139
     * Sets a model that implements the interface Grido\DataSources\IDataSource or data-source object.
140
     * @param mixed $model
141
     * @param bool $forceWrapper
142
     * @throws Exception
143
     * @return Grid
144
     */
145
    public function setModel($model, $forceWrapper = FALSE)
146
    {
147
        $this->model = $model instanceof DataSources\IDataSource && $forceWrapper === FALSE
148
            ? $model
149
            : new DataSources\Model($model);
150
151
        return $this;
152
    }
153
154
    /**
155
     * Sets the default number of items per page.
156
     * @param int $perPage
157
     * @return Grid
158
     */
159
    public function setDefaultPerPage($perPage)
160
    {
161
        $perPage = (int) $perPage;
162
        $this->defaultPerPage = $perPage;
163
164
        if (!in_array($perPage, $this->perPageList)) {
165
            $this->perPageList[] = $perPage;
166
            sort($this->perPageList);
167
        }
168
169
        return $this;
170
    }
171
172
    /**
173
     * Sets default filtering.
174
     * @param array $filter
175
     * @return Grid
176
     */
177
    public function setDefaultFilter(array $filter)
178
    {
179 1
        $this->defaultFilter = array_merge($this->defaultFilter, $filter);
180 1
        return $this;
181
    }
182
183
    /**
184
     * Sets default sorting.
185
     * @param array $sort
186
     * @return Grid
187
     * @throws Exception
188
     */
189
    public function setDefaultSort(array $sort)
190
    {
191 1
        static $replace = ['asc' => Column::ORDER_ASC, 'desc' => Column::ORDER_DESC];
192
193 1
        foreach ($sort as $column => $dir) {
194 1
            $dir = strtr(strtolower($dir), $replace);
195 1
            if (!in_array($dir, $replace)) {
196
                throw new Exception("Dir '$dir' for column '$column' is not allowed.");
197
            }
198
199 1
            $this->defaultSort[$column] = $dir;
200 1
        }
201
202 1
        return $this;
203
    }
204
205
    /**
206
     * Sets items to per-page select.
207 1
     * @param array $perPageList
208
     * @return Grid
209
     */
210
    public function setPerPageList(array $perPageList)
211
    {
212
        $this->perPageList = $perPageList;
213
214
        if ($this->hasFilters(FALSE) || $this->hasOperation(FALSE)) {
215
            $this['form']['count']->setItems($this->getItemsForCountSelect());
216
        }
217
218
        return $this;
219
    }
220
221
    /**
222
     * Sets translator.
223
     * @param \Nette\Localization\ITranslator $translator
224
     * @return Grid
225
     */
226
    public function setTranslator(\Nette\Localization\ITranslator $translator)
227
    {
228 1
        $this->translator = $translator;
229 1
        return $this;
230
    }
231
232
    /**
233
     * Sets type of filter rendering.
234
     * Defaults inner (Filter::RENDER_INNER) if column does not exist then outer filter (Filter::RENDER_OUTER).
235
     * @param string $type
236
     * @throws Exception
237
     * @return Grid
238
     */
239
    public function setFilterRenderType($type)
240
    {
241
        $type = strtolower($type);
242
        if (!in_array($type, [Filter::RENDER_INNER, Filter::RENDER_OUTER])) {
243
            throw new Exception('Type must be Filter::RENDER_INNER or Filter::RENDER_OUTER.');
244
        }
245
246
        $this->filterRenderType = $type;
247
        return $this;
248
    }
249
250
    /**
251
     * Sets custom paginator.
252
     * @param Paginator $paginator
253
     * @return Grid
254
     */
255
    public function setPaginator(Paginator $paginator)
256
    {
257
        $this->paginator = $paginator;
258
        return $this;
259
    }
260
261
    /**
262
     * Sets grid primary key.
263
     * Defaults is "id".
264
     * @param string $key
265
     * @return Grid
266
     */
267
    public function setPrimaryKey($key)
268
    {
269
        $this->primaryKey = $key;
270
        return $this;
271
    }
272
273
    /**
274
     * Sets file name of custom template.
275
     * @param string $file
276
     * @return Grid
277
     */
278
    public function setTemplateFile($file)
279
    {
280
        $this->onRender[] = function() use ($file) {
281
            $this->getTemplate()->add('gridoTemplate', $this->getTemplate()->getFile());
282
            $this->getTemplate()->setFile($file);
283
        };
284
285
        return $this;
286
    }
287
288
    /**
289
     * Sets saving state to session.
290
     * @param bool $state
291
     * @param string $sectionName
292
     * @return Grid
293
     */
294
    public function setRememberState($state = TRUE, $sectionName = NULL)
295
    {
296
        $this->getPresenter(); //component must be attached to presenter
297
        $this->getRememberSession(TRUE); //start session if not
298
        $this->rememberState = (bool) $state;
299
        $this->rememberStateSectionName = $sectionName;
300
301
        return $this;
302
    }
303
304
    /**
305
     * Sets callback for customizing tr html object.
306
     * Callback returns tr html element; function($row, Html $tr).
307
     * @param $callback
308
     * @return Grid
309
     */
310
    public function setRowCallback($callback)
311
    {
312
        $this->rowCallback = $callback;
313
        return $this;
314
    }
315
316
    /**
317
     * Sets client-side options.
318
     * @param array $options
319
     * @return Grid
320
     */
321
    public function setClientSideOptions(array $options)
322
    {
323
        $this->options[self::CLIENT_SIDE_OPTIONS] = $options;
324
        return $this;
325
    }
326
327
    /**
328
     * Determines whether any user error will cause a notice.
329
     * @param bool $mode
330
     * @return \Grido\Grid
331
     */
332
    public function setStrictMode($mode)
333
    {
334
        $this->strictMode = (bool) $mode;
335
        return $this;
336
    }
337
338
    /**
339
     * @param \Grido\Customization $customization
340
     */
341
    public function setCustomization(Customization $customization)
342
    {
343
        $this->customization = $customization;
344
    }
345
346
    /**********************************************************************************************/
347
348
    /**
349
     * Returns total count of data.
350
     * @return int
351
     */
352
    public function getCount()
353
    {
354
        if ($this->count === NULL) {
355
            $this->count = $this->getModel()->getCount();
356
        }
357
358
        return $this->count;
359
    }
360
361
    /**
362
     * Returns default per page.
363
     * @return int
364
     */
365
    public function getDefaultPerPage()
366
    {
367
        if (!in_array($this->defaultPerPage, $this->perPageList)) {
368
            $this->defaultPerPage = $this->perPageList[0];
369
        }
370
371
        return $this->defaultPerPage;
372
    }
373
374
    /**
375
     * Returns default filter.
376
     * @return array
377
     */
378
    public function getDefaultFilter()
379
    {
380 1
        return $this->defaultFilter;
381
    }
382
383
    /**
384
     * Returns default sort.
385
     * @return array
386
     */
387
    public function getDefaultSort()
388
    {
389 1
        return $this->defaultSort;
390
    }
391
392 1
    /**
393
     * Returns list of possible items per page.
394
     * @return array
395
     */
396
    public function getPerPageList()
397
    {
398
        return $this->perPageList;
399
    }
400
401
    /**
402
     * Returns primary key.
403
     * @return string
404
     */
405
    public function getPrimaryKey()
406
    {
407
        return $this->primaryKey;
408
    }
409
410
    /**
411
     * Returns remember state.
412
     * @return bool
413
     */
414
    public function getRememberState()
415
    {
416
        return $this->rememberState;
417
    }
418
419
    /**
420 1
     * Returns row callback.
421
     * @return callback
422
     */
423
    public function getRowCallback()
424
    {
425
        return $this->rowCallback;
426
    }
427
428
    /**
429
     * Returns items per page.
430
     * @return int
431
     */
432
    public function getPerPage()
433
    {
434
        return $this->perPage === NULL
435
            ? $this->getDefaultPerPage()
436
            : $this->perPage;
437
    }
438
439
    /**
440
     * Returns actual filter values.
441
     * @param string $key
442
     * @return mixed
443
     */
444
    public function getActualFilter($key = NULL)
445
    {
446
        $filter = $this->filter ? $this->filter : $this->defaultFilter;
447
        return $key !== NULL && isset($filter[$key]) ? $filter[$key] : $filter;
448
    }
449
450
    /**
451
     * Returns fetched data.
452
     * @param bool $applyPaging
453
     * @param bool $useCache
454
     * @param bool $fetch
455
     * @throws Exception
456
     * @return array|DataSources\IDataSource|\Nette\Database\Table\Selection
457
     */
458
    public function getData($applyPaging = TRUE, $useCache = TRUE, $fetch = TRUE)
459
    {
460
        if ($this->getModel() === NULL) {
461 1
            throw new Exception('Model cannot be empty, please use method $grid->setModel().');
462
        }
463
464
        $data = $this->data;
465
        if ($data === NULL || $useCache === FALSE) {
466
            $this->applyFiltering();
467
            $this->applySorting();
468
469
            if ($applyPaging) {
470
                $this->applyPaging();
471
            }
472
473
            if ($fetch === FALSE) {
474
                return $this->getModel();
475
            }
476
477
            $data = $this->getModel()->getData();
478
479
            if ($useCache === TRUE) {
480
                $this->data = $data;
481
            }
482
483
            if ($applyPaging && !empty($data) && !in_array($this->page, range(1, $this->getPaginator()->pageCount))) {
484
                $this->__triggerUserNotice("Page is out of range.");
485
                $this->page = 1;
486
            }
487
488
            if (!empty($this->onFetchData)) {
489
                $this->onFetchData($this);
490
            }
491
        }
492
493
        return $data;
494
    }
495
496
    /**
497
     * Returns translator.
498
     * @return Translations\FileTranslator
499
     */
500
    public function getTranslator()
501
    {
502 1
        if ($this->translator === NULL) {
503 1
            $this->setTranslator(new Translations\FileTranslator);
504 1
        }
505
506 1
        return $this->translator;
507
    }
508
509
    /**
510
     * Returns remember session for set expiration, etc.
511
     * @param bool $forceStart - if TRUE, session will be started if not
512
     * @return \Nette\Http\SessionSection|NULL
513
     */
514
    public function getRememberSession($forceStart = FALSE)
515
    {
516
        $presenter = $this->getPresenter();
517
        $session = $presenter->getSession();
518
519
        if (!$session->isStarted() && $forceStart) {
520
            $session->start();
521
        }
522
523
        return $session->isStarted()
524
            ? ($session->getSection($this->rememberStateSectionName ?: ($presenter->name . ':' . $this->getUniqueId())))
525
            : NULL;
526
    }
527
528
    /**
529
     * Returns table html element of grid.
530
     * @return \Nette\Utils\Html
531
     */
532
    public function getTablePrototype()
533
    {
534
        if ($this->tablePrototype === NULL) {
535
            $this->tablePrototype = \Nette\Utils\Html::el('table');
536
            $this->tablePrototype->id($this->getName());
537
        }
538
539
        return $this->tablePrototype;
540
    }
541
542
    /**
543
     * @return string
544
     * @internal
545
     */
546
    public function getFilterRenderType()
547
    {
548
        if ($this->filterRenderType !== NULL) {
549
            return $this->filterRenderType;
550
        }
551
552
        $this->filterRenderType = Filter::RENDER_OUTER;
553
        if ($this->hasColumns() && $this->hasFilters() && $this->hasActions()) {
554
            $this->filterRenderType = Filter::RENDER_INNER;
555
556
            $filters = $this[Filter::ID]->getComponents();
557
            foreach ($filters as $filter) {
558
                if (!$this[Column::ID]->getComponent($filter->name, FALSE)) {
559
                    $this->filterRenderType = Filter::RENDER_OUTER;
560
                    break;
561
                }
562
            }
563
        }
564
565
        return $this->filterRenderType;
566
    }
567
568
    /**
569
     * @return DataSources\IDataSource
570
     */
571
    public function getModel()
572
    {
573
        return $this->model;
574
    }
575
576
    /**
577
     * @return Paginator
578
     * @internal
579
     */
580
    public function getPaginator()
581
    {
582
        if ($this->paginator === NULL) {
583
            $this->paginator = new Paginator;
584
            $this->paginator->setItemsPerPage($this->getPerPage())
585
                ->setGrid($this);
586
        }
587
588
        return $this->paginator;
589
    }
590
591
    /**
592
     * A simple wrapper around symfony/property-access with Nette Database dot notation support.
593
     * @param array|object $object
594
     * @param string $name
595
     * @return mixed
596
     * @internal
597
     */
598
    public function getProperty($object, $name)
599
    {
600 1
        if ($object instanceof \Nette\Database\Table\IRow && \Nette\Utils\Strings::contains($name, '.')) {
601
            $parts = explode('.', $name);
602
            foreach ($parts as $item) {
603
                if (is_object($object)) {
604
                    $object = $object->$item;
605
                }
606
            }
607
608
            return $object;
609
        }
610
611 1
        if (is_array($object)) {
612 1
            $name = "[$name]";
613 1
        }
614
615 1
        return $this->getPropertyAccessor()->getValue($object, $name);
616
    }
617
618
    /**
619
     * @return PropertyAccessor
620
     * @internal
621
     */
622
    public function getPropertyAccessor()
623
    {
624 1
        if ($this->propertyAccessor === NULL) {
625 1
            $this->propertyAccessor = new PropertyAccessor(TRUE, TRUE);
626 1
        }
627
628 1
        return $this->propertyAccessor;
629
    }
630
631
    /**
632
     * @param mixed $row item from db
633
     * @return \Nette\Utils\Html
634
     * @internal
635
     */
636
    public function getRowPrototype($row)
637
    {
638
        try {
639
            $primaryValue = $this->getProperty($row, $this->getPrimaryKey());
640
        } catch (\Exception $e) {
641
            $primaryValue = NULL;
642
        }
643
644
        $tr = \Nette\Utils\Html::el('tr');
645
        $primaryValue ? $tr->class[] = "grid-row-$primaryValue" : NULL;
646
647
        if ($this->rowCallback) {
648
            $tr = call_user_func_array($this->rowCallback, [$row, $tr]);
649
        }
650
651
        return $tr;
652
    }
653
654
    /**
655
     * Returns client-side options.
656
     * @return array
657
     */
658
    public function getClientSideOptions()
659
    {
660
        return (array) $this->options[self::CLIENT_SIDE_OPTIONS];
661
    }
662
663
    /**
664
     * @return bool
665
     */
666
    public function isStrictMode()
667
    {
668
        return $this->strictMode;
669
    }
670
671
    /**
672
     * @return Customization
673
     */
674
    public function getCustomization()
675
    {
676
        if ($this->customization === NULL) {
677
            $this->customization = new Customization($this);
678
        }
679
680
        return $this->customization;
681
    }
682
683
    /**********************************************************************************************/
684
685
    /**
686
     * Loads state informations.
687
     * @param array $params
688
     * @internal
689
     */
690
    public function loadState(array $params)
691
    {
692
        //loads state from session
693
        $session = $this->getRememberSession();
694
        if ($session && $this->getPresenter()->isSignalReceiver($this)) {
695
            $session->remove();
696
        } elseif ($session && empty($params) && $session->params) {
697
            $params = (array) $session->params;
698
        }
699
700
        parent::loadState($params);
701
    }
702
703
    /**
704
     * Saves state informations for next request.
705
     * @param array $params
706
     * @param \Nette\Application\UI\PresenterComponentReflection $reflection (internal, used by Presenter)
707
     * @internal
708
     */
709
    public function saveState(array &$params, $reflection = NULL)
710
    {
711
        !empty($this->onRegistered) && $this->onRegistered($this);
712
        return parent::saveState($params, $reflection);
0 ignored issues
show
Unused Code introduced by
The call to Container::saveState() has too many arguments starting with $reflection.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
713
    }
714
715
    /**
716
     * Ajax method.
717
     * @internal
718
     */
719
    public function handleRefresh()
720
    {
721
        $this->reload();
722
    }
723
724
    /**
725
     * @param int $page
726
     * @internal
727
     */
728
    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...
729
    {
730
        $this->reload();
731
    }
732
733
    /**
734
     * @param array $sort
735
     * @internal
736
     */
737
    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...
738
    {
739
        $this->page = 1;
740
        $this->reload();
741
    }
742
743
    /**
744
     * @param \Nette\Forms\Controls\SubmitButton $button
745
     * @internal
746
     */
747
    public function handleFilter(\Nette\Forms\Controls\SubmitButton $button)
748
    {
749
        $values = $button->form->values[Filter::ID];
750
        $session = $this->rememberState //session filter
751
            ? isset($this->getRememberSession(TRUE)->params['filter'])
752
                ? $this->getRememberSession(TRUE)->params['filter']
753
                : []
754
            : [];
755
756
        foreach ($values as $name => $value) {
757
            if (is_numeric($value) || !empty($value) || isset($this->defaultFilter[$name]) || isset($session[$name])) {
758
                $this->filter[$name] = $this->getFilter($name)->changeValue($value);
759
            } elseif (isset($this->filter[$name])) {
760
                unset($this->filter[$name]);
761
            }
762
        }
763
764
        $this->page = 1;
765
        $this->reload();
766
    }
767
768
    /**
769
     * @param \Nette\Forms\Controls\SubmitButton $button
770
     * @internal
771
     */
772
    public function handleReset(\Nette\Forms\Controls\SubmitButton $button)
773
    {
774
        $this->sort = [];
775
        $this->filter = [];
776
        $this->perPage = NULL;
777
778
        if ($session = $this->getRememberSession()) {
779
            $session->remove();
780
        }
781
782
        $button->form->setValues([Filter::ID => $this->defaultFilter], TRUE);
783
784
        $this->page = 1;
785
        $this->reload();
786
    }
787
788
    /**
789
     * @param \Nette\Forms\Controls\SubmitButton $button
790
     * @internal
791
     */
792
    public function handlePerPage(\Nette\Forms\Controls\SubmitButton $button)
793
    {
794
        $perPage = (int) $button->form['count']->value;
795
        $this->perPage = $perPage == $this->defaultPerPage
796
            ? NULL
797
            : $perPage;
798
799
        $this->page = 1;
800
        $this->reload();
801
    }
802
803
    /**
804
     * Refresh wrapper.
805
     * @return void
806
     * @internal
807
     */
808
    public function reload()
809
    {
810
        if ($this->presenter->isAjax()) {
811
            $this->presenter->payload->grido = TRUE;
812
            $this->redrawControl();
813
        } else {
814
            $this->redirect('this');
815
        }
816
    }
817
818
    /**********************************************************************************************/
819
820
    /**
821
     * @return \Nette\Templating\FileTemplate
822
     * @internal
823
     */
824
    public function createTemplate()
825
    {
826
        $template = parent::createTemplate();
827
        $template->setFile($this->getCustomization()->getTemplateFiles()[Customization::TEMPLATE_DEFAULT]);
828
        $template->getLatte()->addFilter('translate', [$this->getTranslator(), 'translate']);
829
830
        return $template;
831
    }
832
833
    /**
834
     * @internal
835
     * @throws Exception
836
     */
837
    public function render()
838
    {
839
        if (!$this->hasColumns()) {
840
            throw new Exception('Grid must have defined a column, please use method $grid->addColumn*().');
841
        }
842
843
        $this->saveRememberState();
844
        $data = $this->getData();
845
846
        if (!empty($this->onRender)) {
847
            $this->onRender($this);
848
        }
849
850
        $form = $this['form'];
851
852
        $this->getTemplate()->add('data', $data);
853
        $this->getTemplate()->add('form', $form);
854
        $this->getTemplate()->add('paginator', $this->getPaginator());
855
        $this->getTemplate()->add('customization', $this->getCustomization());
856
        $this->getTemplate()->add('columns', $this->getComponent(Column::ID)->getComponents());
857
        $this->getTemplate()->add('actions', $this->hasActions()
858
            ? $this->getComponent(Action::ID)->getComponents()
859
            : []
860
        );
861
862
        $this->getTemplate()->add('buttons', $this->hasButtons()
863
            ? $this->getComponent(Button::ID)->getComponents()
864
            : []
865
        );
866
867
        $this->getTemplate()->add('formFilters', $this->hasFilters()
868
            ? $form->getComponent(Filter::ID)->getComponents()
869
            : []
870
        );
871
872
        $form['count']->setValue($this->getPerPage());
873
874
        if ($options = $this->options[self::CLIENT_SIDE_OPTIONS]) {
875
            $this->getTablePrototype()->setAttribute('data-' . self::CLIENT_SIDE_OPTIONS, json_encode($options));
876
        }
877
878
        $this->getTemplate()->render();
879
    }
880
881
    protected function saveRememberState()
882
    {
883
        if ($this->rememberState) {
884
            $session = $this->getRememberSession(TRUE);
885
            $params = array_keys($this->getReflection()->getPersistentParams());
886
            foreach ($params as $param) {
887
                $session->params[$param] = $this->$param;
888
            }
889
        }
890
    }
891
892
    protected function applyFiltering()
893
    {
894
        $conditions = $this->__getConditions($this->getActualFilter());
895
        $this->getModel()->filter($conditions);
896
    }
897
898
    /**
899
     * @param array $filter
900
     * @return array
901
     * @internal
902
     */
903
    public function __getConditions(array $filter)
904
    {
905
        $conditions = [];
906
        if (!empty($filter)) {
907
            try {
908
                $this['form']->setDefaults([Filter::ID => $filter]);
909
            } catch (\Nette\InvalidArgumentException $e) {
910
                $this->__triggerUserNotice($e->getMessage());
911
                $filter = [];
912
                if ($session = $this->getRememberSession()) {
913
                    $session->remove();
914
                }
915
            }
916
917
            foreach ($filter as $column => $value) {
918
                if ($component = $this->getFilter($column, FALSE)) {
919
                    if ($condition = $component->__getCondition($value)) {
920
                        $conditions[] = $condition;
921
                    }
922
                } else {
923
                    $this->__triggerUserNotice("Filter with name '$column' does not exist.");
924
                }
925
            }
926
        }
927
928
        return $conditions;
929
    }
930
931
    protected function applySorting()
932
    {
933
        $sort = [];
934
        $this->sort = $this->sort ? $this->sort : $this->defaultSort;
935
936
        foreach ($this->sort as $column => $dir) {
937
            $component = $this->getColumn($column, FALSE);
938
            if (!$component) {
939
                if (!isset($this->defaultSort[$column])) {
940
                    $this->__triggerUserNotice("Column with name '$column' does not exist.");
941
                    break;
942
                }
943
944
            } elseif (!$component->isSortable()) {
945
                if (isset($this->defaultSort[$column])) {
946
                    $component->setSortable();
947
                } else {
948
                    $this->__triggerUserNotice("Column with name '$column' is not sortable.");
949
                    break;
950
                }
951
            }
952
953
            if (!in_array($dir, [Column::ORDER_ASC, Column::ORDER_DESC])) {
954
                if ($dir == '' && isset($this->defaultSort[$column])) {
955
                    unset($this->sort[$column]);
956
                    break;
957
                }
958
959
                $this->__triggerUserNotice("Dir '$dir' is not allowed.");
960
                break;
961
            }
962
963
            $sort[$component ? $component->column : $column] = $dir == Column::ORDER_ASC ? 'ASC' : 'DESC';
0 ignored issues
show
Bug introduced by
The property column cannot be accessed from this context as it is declared protected in class Grido\Components\Columns\Column.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
964
        }
965
966
        if (!empty($sort)) {
967
            $this->getModel()->sort($sort);
968
        }
969
    }
970
971
    protected function applyPaging()
972
    {
973
        $paginator = $this->getPaginator()
974
            ->setItemCount($this->getCount())
975
            ->setPage($this->page);
976
977
        $perPage = $this->getPerPage();
978
        if ($perPage !== NULL && !in_array($perPage, $this->perPageList)) {
979
            $this->__triggerUserNotice("The number '$perPage' of items per page is out of range.");
980
        }
981
982
        $this->getModel()->limit($paginator->getOffset(), $paginator->getLength());
983
    }
984
985
    protected function createComponentForm($name)
986
    {
987 1
        $form = new \Nette\Application\UI\Form($this, $name);
988 1
        $form->setTranslator($this->getTranslator());
989 1
        $form->setMethod($form::GET);
990
991 1
        $buttons = $form->addContainer(self::BUTTONS);
992 1
        $buttons->addSubmit('search', 'Grido.Search')
993 1
            ->onClick[] = [$this, 'handleFilter'];
994 1
        $buttons->addSubmit('reset', 'Grido.Reset')
995 1
            ->onClick[] = [$this, 'handleReset'];
996 1
        $buttons->addSubmit('perPage', 'Grido.ItemsPerPage')
997 1
            ->onClick[] = [$this, 'handlePerPage'];
998
999 1
        $form->addSelect('count', 'Count', $this->getItemsForCountSelect())
1000 1
            ->setTranslator(NULL)
1001 1
            ->controlPrototype->attrs['title'] = $this->getTranslator()->translate('Grido.ItemsPerPage');
1002 1
    }
1003
1004
    /**
1005
     * @return array
1006
     */
1007
    protected function getItemsForCountSelect()
1008
    {
1009 1
        return array_combine($this->perPageList, $this->perPageList);
1010
    }
1011
1012
    /**
1013
     * @internal
1014
     * @param string $message
1015
     */
1016
    public function __triggerUserNotice($message)
1017
    {
1018
        if ($this->getPresenter(FALSE) && $session = $this->getRememberSession()) {
1019
            $session->remove();
1020
        }
1021
1022
        $this->strictMode && trigger_error($message, E_USER_NOTICE);
1023
    }
1024
}
1025