Completed
Push — master ( 4537f5...825efc )
by Anton
8s
created

Grid::getParams()   C

Complexity

Conditions 8
Paths 32

Size

Total Lines 44
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 8

Importance

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