Completed
Push — master ( 9ee4de...ccfc27 )
by Anton
18s
created

Grid::getAllowOrders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link      https://github.com/bluzphp/framework
7
 */
8
9
declare(strict_types=1);
10
11
namespace Bluz\Grid;
12
13
use Bluz\Common\Helper;
14
use Bluz\Common\Options;
15
use Bluz\Proxy\Request;
16
use Bluz\Proxy\Router;
17
18
/**
19
 * Grid
20
 *
21
 * @package  Bluz\Grid
22
 * @author   Anton Shevchuk
23
 * @link     https://github.com/bluzphp/framework/wiki/Grid
24
 *
25
 * @method string filter($column, $filter, $value, $reset = true)
26
 * @method string first()
27
 * @method string last()
28
 * @method string limit($limit = 25)
29
 * @method string next()
30
 * @method string order($column, $order = null, $defaultOrder = Grid::ORDER_ASC, $reset = true)
31
 * @method string page($page = 1)
32
 * @method string pages()
33
 * @method string prev()
34
 * @method string reset()
35
 * @method string total()
36
 */
37
abstract class Grid
38
{
39
    use Options;
40
    use Helper;
41
42
    const ORDER_ASC = 'asc';
43
    const ORDER_DESC = 'desc';
44
45
    const FILTER_LIKE = 'like'; // like
46
    const FILTER_ENUM = 'enum'; // one from .., .., ..
47
48
    const FILTER_EQ = 'eq'; // equal to ..
49
    const FILTER_NE = 'ne'; // not equal to ..
50
    const FILTER_GT = 'gt'; // greater than ..
51
    const FILTER_GE = 'ge'; // greater than .. or equal
52
    const FILTER_LT = 'lt'; // less than ..
53
    const FILTER_LE = 'le'; // less than .. or equal
54
55
    /**
56
     * @var Source\AbstractSource instance of Source
57
     */
58
    protected $adapter;
59
60
    /**
61
     * @var Data instance of Data
62
     */
63
    protected $data;
64
65
    /**
66
     * @var string unique identification of grid
67
     */
68
    protected $uid;
69
70
    /**
71
     * @var string unique prefix of grid
72
     */
73
    protected $prefix = '';
74
75
    /**
76
     * @var string location of Grid
77
     */
78
    protected $module;
79
80
    /**
81
     * @var string location of Grid
82
     */
83
    protected $controller;
84
85
    /**
86
     * @var array custom array params
87
     */
88
    protected $params = [];
89
90
    /**
91
     * @var integer start from first page
92
     */
93
    protected $page = 1;
94
95
    /**
96
     * @var integer limit per page
97
     */
98
    protected $limit = 25;
99
100
    /**
101
     * @var integer default value of page limit
102
     * @see Grid::$limit
103
     */
104
    protected $defaultLimit = 25;
105
106
    /**
107
     * List of orders
108
     *
109
     * Example
110
     *     'first' => 'ASC',
111
     *     'last' => 'ASC'
112
     *
113
     * @var array
114
     */
115
    protected $orders = [];
116
117
    /**
118
     * @var array default order
119
     * @see Grid::$orders
120
     */
121
    protected $defaultOrder;
122
123
    /**
124
     * @var array list of allow orders
125
     * @see Grid::$orders
126
     */
127
    protected $allowOrders = [];
128
129
    /**
130
     * @var array list of filters
131
     */
132
    protected $filters = [];
133
134
    /**
135
     * List of allow filters
136
     *
137
     * Example
138
     *     ['id', 'status' => ['active', 'disable']]
139
     *
140
     * @var array
141
     * @see Grid::$filters
142
     */
143
    protected $allowFilters = [];
144
145
    /**
146
     * List of allow filter names
147
     *
148
     * @var array
149
     * @see Grid::$filters
150
     */
151
    protected $allowFilterNames = [
152
        self::FILTER_LIKE,
153
        self::FILTER_ENUM,
154
        self::FILTER_EQ,
155
        self::FILTER_NE,
156
        self::FILTER_GT,
157
        self::FILTER_GE,
158
        self::FILTER_LT,
159
        self::FILTER_LE
160
    ];
161
162
    /**
163
     * List of aliases for columns in DB
164
     *
165
     * @var array
166
     */
167
    protected $aliases = [];
168
169
    /**
170
     * Grid constructor
171
     *
172
     * @param array $options
173
     */
174 34
    public function __construct($options = null)
175
    {
176 34
        if ($options) {
177
            $this->setOptions($options);
178
        }
179
180 34
        if ($this->getUid()) {
181 34
            $this->prefix = $this->getUid() . '-';
182
        }
183
184 34
        $this->init();
185
186 34
        $this->processRequest();
187
188
        // initial default helper path
189 34
        $this->addHelperPath(__DIR__ . '/Helper/');
190 34
    }
191
192
    /**
193
     * Initialize Grid
194
     *
195
     * @return Grid
196
     */
197
    abstract public function init();
198
199
    /**
200
     * Set source adapter
201
     *
202
     * @param Source\AbstractSource $adapter
203
     *
204
     * @return void
205
     */
206 34
    public function setAdapter(Source\AbstractSource $adapter)
207
    {
208 34
        $this->adapter = $adapter;
209 34
    }
210
211
    /**
212
     * Get source adapter
213
     *
214
     * @return Source\AbstractSource
215
     * @throws GridException
216
     */
217 15
    public function getAdapter()
218
    {
219 15
        if (null === $this->adapter) {
220
            throw new GridException('Grid adapter is not initialized');
221
        }
222 15
        return $this->adapter;
223
    }
224
225
    /**
226
     * Get unique Grid Id
227
     *
228
     * @return string
229
     */
230 34
    public function getUid()
231
    {
232 34
        return $this->uid;
233
    }
234
235
    /**
236
     * Get prefix
237
     *
238
     * @return string
239
     */
240 1
    public function getPrefix()
241
    {
242 1
        return $this->prefix;
243
    }
244
245
    /**
246
     * Set module
247
     *
248
     * @param string $module
249
     *
250
     * @return void
251
     */
252 1
    public function setModule($module)
253
    {
254 1
        $this->module = $module;
255 1
    }
256
257
    /**
258
     * Get module
259
     *
260
     * @return string
261
     */
262 13
    public function getModule()
263
    {
264 13
        return $this->module;
265
    }
266
267
    /**
268
     * Set controller
269
     *
270
     * @param  string $controller
271
     *
272
     * @return void
273
     */
274 1
    public function setController($controller)
275
    {
276 1
        $this->controller = $controller;
277 1
    }
278
279
    /**
280
     * Get controller
281
     *
282
     * @return string
283
     */
284 13
    public function getController()
285
    {
286 13
        return $this->controller;
287
    }
288
289
    /**
290
     * Process request
291
     *
292
     * Example of request url
293
     * - http://domain.com/pages/grid/
294
     * - http://domain.com/pages/grid/page/2/
295
     * - http://domain.com/pages/grid/page/2/order-alias/desc/
296
     * - http://domain.com/pages/grid/page/2/order-created/desc/order-alias/asc/
297
     *
298
     * with prefix for support more than one grid on page
299
     * - http://domain.com/users/grid/users-page/2/users-order-created/desc/
300
     * - http://domain.com/users/grid/users-page/2/users-filter-status/active/
301
     *
302
     * hash support
303
     * - http://domain.com/pages/grid/#/page/2/order-created/desc/order-alias/asc/
304
     *
305
     * @return Grid
306
     */
307 34
    public function processRequest()
308
    {
309 34
        $this->module = Request::getModule();
310 34
        $this->controller = Request::getController();
311
312 34
        $page = (int)Request::getParam($this->prefix . 'page', 1);
313 34
        $this->setPage($page);
314
315 34
        $limit = (int)Request::getParam($this->prefix . 'limit', $this->limit);
316 34
        $this->setLimit($limit);
317
318 34
        foreach ($this->allowOrders as $column) {
319 34
            $alias = $this->applyAlias($column);
320 34
            $order = Request::getParam($this->prefix . 'order-' . $alias);
321 34
            if (!is_null($order)) {
322 34
                $this->addOrder($column, $order);
323
            }
324
        }
325 34
        foreach ($this->allowFilters as $column) {
326 34
            $alias = $this->applyAlias($column);
327 34
            $filter = Request::getParam($this->prefix . 'filter-' . $alias);
328
329 34
            if (!is_null($filter)) {
330 1
                $filter = trim($filter, ' -');
331 1
                if (strpos($filter, '-')) {
332
                    /**
333
                     * Example of filters
334
                     * - http://domain.com/users/grid/users-filter-roleId/gt-2      - roleId greater than 2
335
                     * - http://domain.com/users/grid/users-filter-roleId/gt-1-lt-4 - 1 < roleId < 4
336
                     * - http://domain.com/users/grid/users-filter-login/eq-admin   - login == admin
337
                     */
338 1
                    while ($filter) {
339 1
                        list($filterName, $filterValue, $filter) = array_pad(explode('-', $filter, 3), 3, null);
340 1
                        $this->addFilter($column, $filterName, $filterValue);
341
                    }
342
                } else {
343
                    /**
344
                     * Example of filters
345
                     * - http://domain.com/users/grid/users-filter-roleId/2
346
                     * - http://domain.com/users/grid/users-filter-login/admin
347
                     */
348 34
                    $this->addFilter($column, self::FILTER_EQ, $filter);
349
                }
350
            }
351
        }
352
353 34
        return $this;
354
    }
355
356
    /**
357
     * Process source
358
     *
359
     * @return self
360
     * @throws GridException
361
     */
362 15
    public function processSource()
363
    {
364 15
        if (null === $this->adapter) {
365
            throw new GridException('Grid Adapter is not initiated, please update method `init()` and try again');
366
        }
367
368
        try {
369 15
            $this->data = $this->getAdapter()->process($this->getSettings());
370
        } catch (\Exception $e) {
371
            throw new GridException('Grid Adapter can\'t process request: '. $e->getMessage());
372
        }
373
374 15
        return $this;
375
    }
376
377
    /**
378
     * Get data
379
     *
380
     * @return Data
381
     */
382 15
    public function getData()
383
    {
384 15
        if (!$this->data) {
385 15
            $this->processSource();
386
        }
387 15
        return $this->data;
388
    }
389
390
    /**
391
     * Get settings
392
     *
393
     * @return array
394
     */
395 15
    public function getSettings()
396
    {
397
        $settings = [
398 15
            'page' => $this->getPage(),
399 15
            'limit' => $this->getLimit(),
400 15
            'orders' => $this->getOrders(),
401 15
            'filters' => $this->getFilters()
402
        ];
403 15
        return $settings;
404
    }
405
406
    /**
407
     * Setup params
408
     *
409
     * @param  $params
410
     *
411
     * @return void
412
     */
413
    public function setParams($params)
414
    {
415
        $this->params = $params;
416
    }
417
418
    /**
419
     * Return params prepared for url builder
420
     *
421
     * @param  array $rewrite
422
     *
423
     * @return array
424
     */
425 13
    public function getParams(array $rewrite = [])
426
    {
427 13
        $params = $this->params;
428
429
        // change page to first for each new grid (with new filters or orders, or other stuff)
430 13
        $page = $rewrite['page'] ?? 1;
431
432 13
        if ($page > 1) {
433 4
            $params[$this->prefix . 'page'] = $page;
434
        }
435
436
        // change limit
437 13
        $limit = $rewrite['limit'] ?? $this->getLimit();
438
439 13
        if ($limit != $this->defaultLimit) {
440 1
            $params[$this->prefix . 'limit'] = $limit;
441
        }
442
443
        // change orders
444 13
        $orders = $rewrite['orders'] ?? $this->getOrders();
445
446 13
        foreach ($orders as $column => $order) {
447 3
            $column = $this->applyAlias($column);
448 3
            $params[$this->prefix . 'order-' . $column] = $order;
449
        }
450
451
        // change filters
452 13
        $filters = $rewrite['filters'] ?? $this->getFilters();
453
454 13
        foreach ($filters as $column => $columnFilters) {
455 3
            $column = $this->applyAlias($column);
456 3
            if (count($columnFilters) == 1 && isset($columnFilters[self::FILTER_EQ])) {
457 2
                $params[$this->prefix . 'filter-' . $column] = $columnFilters[self::FILTER_EQ];
458 2
                continue;
459
            }
460
461 2
            $columnFilter = [];
462 2
            foreach ($columnFilters as $filterName => $filterValue) {
463 2
                $columnFilter[] = $filterName . '-' . $filterValue;
464
            }
465 2
            $params[$this->prefix . 'filter-' . $column] = implode('-', $columnFilter);
466
        }
467 13
        return $params;
468
    }
469
470
    /**
471
     * Get Url
472
     *
473
     * @param  array $params
474
     *
475
     * @return string
476
     */
477 13
    public function getUrl($params)
478
    {
479
        // prepare params
480 13
        $params = $this->getParams($params);
481
482
        // retrieve URL
483 13
        return Router::getUrl(
484 13
            $this->getModule(),
485 13
            $this->getController(),
486 13
            $params
487
        );
488
    }
489
490
    /**
491
     * Add column name for allow order
492
     *
493
     * @param string $column
494
     *
495
     * @return void
496
     */
497 34
    public function addAllowOrder($column)
498
    {
499 34
        $this->allowOrders[] = $column;
500 34
    }
501
502
    /**
503
     * Set allow orders
504
     *
505
     * @param  string[] $orders
506
     *
507
     * @return void
508
     */
509 34
    public function setAllowOrders(array $orders = [])
510
    {
511 34
        $this->allowOrders = [];
512 34
        foreach ($orders as $column) {
513 34
            $this->addAllowOrder($column);
514
        }
515 34
    }
516
517
    /**
518
     * Get allow orders
519
     *
520
     * @return array
521
     */
522 8
    public function getAllowOrders()
523
    {
524 8
        return $this->allowOrders;
525
    }
526
527
    /**
528
     * Check order column
529
     *
530
     * @param  string $column
531
     *
532
     * @return bool
533
     */
534 8
    protected function checkOrderColumn($column)
535
    {
536 8
        return in_array($column, $this->getAllowOrders());
537
    }
538
539
    /**
540
     * Check order name
541
     *
542
     * @param  string $order
543
     *
544
     * @return bool
545
     */
546 6
    protected function checkOrderName($order)
547
    {
548 6
        return ($order == Grid::ORDER_ASC || $order == Grid::ORDER_DESC);
549
    }
550
551
    /**
552
     * Add order rule
553
     *
554
     * @param  string $column
555
     * @param  string $order
556
     *
557
     * @return void
558
     * @throws GridException
559
     */
560 8
    public function addOrder($column, $order = Grid::ORDER_ASC)
561
    {
562 8
        if (!$this->checkOrderColumn($column)) {
563 1
            throw new GridException("Order for column `$column` is not allowed");
564
        }
565
566 7
        if (!$this->checkOrderName($order)) {
567 1
            throw new GridException("Order name for column `$column` is incorrect");
568
        }
569
570 6
        $this->orders[$column] = $order;
571 6
    }
572
573
    /**
574
     * Add order rules
575
     *
576
     * @param  array $orders
577
     *
578
     * @return void
579
     */
580 1
    public function addOrders(array $orders)
581
    {
582 1
        foreach ($orders as $column => $order) {
583 1
            $this->addOrder($column, $order);
584
        }
585 1
    }
586
587
    /**
588
     * Set order
589
     *
590
     * @param  string $column
591
     * @param  string $order ASC or DESC
592
     *
593
     * @return void
594
     */
595 6
    public function setOrder($column, $order = Grid::ORDER_ASC)
596
    {
597 6
        $this->orders = [];
598 6
        $this->addOrder($column, $order);
599 4
    }
600
601
    /**
602
     * Set orders
603
     *
604
     * @param  array $orders
605
     *
606
     * @return void
607
     */
608 1
    public function setOrders(array $orders)
609
    {
610 1
        $this->orders = [];
611 1
        $this->addOrders($orders);
612 1
    }
613
614
    /**
615
     * Get orders
616
     *
617
     * @return array
618
     */
619 25
    public function getOrders()
620
    {
621 25
        $default = $this->getDefaultOrder();
622
623
        // remove default order when another one is set
624 25
        if (is_array($default)
625 25
            && count($this->orders)
626 25
            && isset($this->orders[key($default)])
627 25
            && $this->orders[key($default)] === reset($default)
628
        ) {
629 3
            unset($this->orders[key($default)]);
630
        }
631
632 25
        return $this->orders;
633
    }
634
635
    /**
636
     * Add column name to allow filter it
637
     *
638
     * @param string $column
639
     *
640
     * @return void
641
     */
642 34
    public function addAllowFilter($column)
643
    {
644 34
        $this->allowFilters[] = $column;
645 34
    }
646
647
    /**
648
     * Set allowed filters
649
     *
650
     * @param  string[] $filters
651
     *
652
     * @return void
653
     */
654 34
    public function setAllowFilters(array $filters = [])
655
    {
656 34
        $this->allowFilters = [];
657 34
        foreach ($filters as $column) {
658 34
            $this->addAllowFilter($column);
659
        }
660 34
    }
661
662
    /**
663
     * Get allow filters
664
     *
665
     * @return array
666
     */
667 12
    public function getAllowFilters()
668
    {
669 12
        return $this->allowFilters;
670
    }
671
672
    /**
673
     * Check filter column
674
     *
675
     * @param  string $column
676
     *
677
     * @return bool
678
     */
679 12
    protected function checkFilterColumn($column)
680
    {
681 12
        if (in_array($column, $this->getAllowFilters()) ||
682 12
            array_key_exists($column, $this->getAllowFilters())
683
        ) {
684 10
            return true;
685
        }
686 2
        return false;
687
    }
688
689
    /**
690
     * Check filter
691
     *
692
     * @param  string $filter
693
     *
694
     * @return bool
695
     */
696 12
    protected function checkFilterName($filter)
697
    {
698 12
        return in_array($filter, $this->allowFilterNames);
699
    }
700
701
    /**
702
     * Add filter
703
     *
704
     * @param  string $column name
705
     * @param  string $filter
706
     * @param  string $value
707
     *
708
     * @return void
709
     * @throws GridException
710
     */
711 10
    public function addFilter($column, $filter, $value)
712
    {
713 10
        if (!$this->checkFilterColumn($column)) {
714 1
            throw new GridException("Filter for column `$column` is not allowed");
715
        }
716 9
        if (!$this->checkFilterName($filter)) {
717 1
            throw new GridException('Filter name is incorrect');
718
        }
719 8
        if (!isset($this->filters[$column])) {
720 8
            $this->filters[$column] = [];
721
        }
722 8
        $this->filters[$column][$filter] = $value;
723 8
    }
724
725
726
    /**
727
     * Get filter
728
     *
729
     * @param  string $column
730
     * @param  string $filter
731
     *
732
     * @return mixed
733
     */
734
    public function getFilter($column, $filter = null)
735
    {
736
        if (null === $filter) {
737
            return $this->filters[$column] ?? null;
738
        }
739
        return $this->filters[$column][$filter] ?? null;
740
    }
741
742
    /**
743
     * Get filters
744
     *
745
     * @return array
746
     */
747 23
    public function getFilters()
748
    {
749 23
        return $this->filters;
750
    }
751
752
    /**
753
     * Add alias for column name
754
     *
755
     * @param  string $column
756
     * @param  string $alias
757
     *
758
     * @return void
759
     */
760 32
    public function addAlias($column, $alias)
761
    {
762 32
        $this->aliases[$column] = $alias;
763 32
    }
764
765
    /**
766
     * Get column name by alias
767
     *
768
     * @param  string $alias
769
     *
770
     * @return string
771
     */
772
    protected function reverseAlias($alias)
773
    {
774
        return array_search($alias, $this->aliases) ?: $alias;
775
    }
776
777
    /**
778
     * Get alias by column name
779
     *
780
     * @param  string $column
781
     *
782
     * @return string
783
     */
784 34
    protected function applyAlias($column)
785
    {
786 34
        return $this->aliases[$column] ?? $column;
787
    }
788
789
    /**
790
     * Set page
791
     *
792
     * @param  integer $page
793
     *
794
     * @return void
795
     * @throws GridException
796
     */
797 34
    public function setPage(int $page = 1)
798
    {
799 34
        if ($page < 1) {
800 1
            throw new GridException('Wrong page number, should be greater than zero');
801
        }
802 34
        $this->page = (int)$page;
803 34
    }
804
805
    /**
806
     * Get page
807
     *
808
     * @return integer
809
     */
810 17
    public function getPage(): int
811
    {
812 17
        return $this->page;
813
    }
814
815
    /**
816
     * Set limit per page
817
     *
818
     * @param  integer $limit
819
     *
820
     * @return void
821
     * @throws GridException
822
     */
823 34
    public function setLimit(int $limit)
824
    {
825 34
        if ($limit < 1) {
826 1
            throw new GridException('Wrong limit value, should be greater than zero');
827
        }
828 34
        $this->limit = (int)$limit;
829 34
    }
830
831
    /**
832
     * Get limit per page
833
     *
834
     * @return integer
835
     */
836 24
    public function getLimit(): int
837
    {
838 24
        return $this->limit;
839
    }
840
841
    /**
842
     * Set default limit
843
     *
844
     * @param  integer $limit
845
     *
846
     * @return void
847
     * @throws GridException
848
     */
849 34
    public function setDefaultLimit(int $limit)
850
    {
851 34
        if ($limit < 1) {
852 1
            throw new GridException('Wrong default limit value, should be greater than zero');
853
        }
854 34
        $this->setLimit($limit);
855
856 34
        $this->defaultLimit = (int)$limit;
857 34
    }
858
859
    /**
860
     * Get default limit
861
     *
862
     * @return integer
863
     */
864 1
    public function getDefaultLimit(): int
865
    {
866 1
        return $this->defaultLimit;
867
    }
868
869
    /**
870
     * Set default order
871
     *
872
     * @param  string $column
873
     * @param  string $order ASC or DESC
874
     *
875
     * @return void
876
     * @throws GridException
877
     */
878 5
    public function setDefaultOrder($column, $order = Grid::ORDER_ASC)
879
    {
880 5
        $this->setOrder($column, $order);
881
882 3
        $this->defaultOrder = [$column => $order];
883 3
    }
884
885
    /**
886
     * Get default order
887
     *
888
     * @return array
889
     */
890 24
    public function getDefaultOrder()
891
    {
892 24
        return $this->defaultOrder;
893
    }
894
}
895