Completed
Push — master ( 369b2c...ffb215 )
by Anton
8s
created

Grid::applyAlias()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 2
eloc 2
nc 2
nop 1
crap 2
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 = array();
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 = array();
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 = array();
130
131
    /**
132
     * @var array list of filters
133
     */
134
    protected $filters = array();
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 = array();
146
147
    /**
148
     * List of aliases for columns in DB
149
     *
150
     * @var array
151
     */
152
    protected $aliases = array();
153
154
    /**
155
     * Grid constructor
156
     *
157
     * @param array $options
158
     */
159 33
    public function __construct($options = null)
160
    {
161 33
        if ($options) {
162
            $this->setOptions($options);
163
        }
164
165 33
        if ($this->uid) {
166 33
            $this->prefix = $this->getUid() . '-';
167
        } else {
168
            $this->prefix = '';
169
        }
170
171 33
        $this->init();
172
173 33
        $this->processRequest();
174
175
        // initial default helper path
176 33
        $this->addHelperPath(dirname(__FILE__) . '/Helper/');
177 33
    }
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 33
    public function setAdapter(Source\AbstractSource $adapter)
193
    {
194 33
        $this->adapter = $adapter;
195 33
    }
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 33
    public function getUid()
217
    {
218 33
        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 11
    public function getModule()
248
    {
249 11
        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 11
    public function getController()
269
    {
270 11
        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 33
    public function processRequest()
292
    {
293 33
        $this->module = Request::getModule();
294 33
        $this->controller = Request::getController();
295
296 33
        $page = Request::getParam($this->prefix . 'page', 1);
297 33
        $this->setPage($page);
298
299 33
        $limit = Request::getParam($this->prefix . 'limit', $this->limit);
300 33
        $this->setLimit($limit);
301
302 33
        foreach ($this->allowOrders as $column) {
303 33
            $order = Request::getParam($this->prefix . 'order-' . $column);
304 33
            if ($order) {
305 33
                $this->addOrder($column, $order);
306
            }
307
        }
308 33
        foreach ($this->allowFilters as $column) {
309 33
            $filter = Request::getParam($this->prefix . 'filter-' . $column);
310
311 33
            if ($filter) {
312 1
                if (strpos($filter, '-')) {
313 1
                    $filter = trim($filter, ' -');
314
315 1
                    while ($pos = strpos($filter, '-')) {
316 1
                        $filterType = substr($filter, 0, $pos);
317 1
                        $filter = substr($filter, $pos + 1);
318
319 1
                        if (strpos($filter, '-')) {
320
                            $filterValue = substr($filter, 0, strpos($filter, '-'));
321
                            $filter = substr($filter, strpos($filter, '-') + 1);
322
                        } else {
323 1
                            $filterValue = $filter;
324
                        }
325
326 1
                        $this->addFilter($column, $filterType, $filterValue);
327
                    }
328
                } else {
329 33
                    $this->addFilter($column, self::FILTER_EQ, $filter);
330
                }
331
            }
332
        }
333 33
        return $this;
334
    }
335
336
    /**
337
     * Process source
338
     *
339
     * @return self
340
     * @throws GridException
341
     */
342 15
    public function processSource()
343
    {
344 15
        if (null === $this->adapter) {
345
            throw new GridException("Grid Adapter is not initiated, please update method init() and try again");
346
        }
347
348
        try {
349 15
            $this->data = $this->getAdapter()->process($this->getSettings());
350
        } catch (\Exception $e) {
351
            throw new GridException("Grid Adapter can't process request: ". $e->getMessage());
352
        }
353
354 15
        return $this;
355
    }
356
357
    /**
358
     * Get data
359
     *
360
     * @return Data
361
     */
362 15
    public function getData()
363
    {
364 15
        if (!$this->data) {
365 15
            $this->processSource();
366
        }
367 15
        return $this->data;
368
    }
369
370
    /**
371
     * Get settings
372
     *
373
     * @return array
374
     */
375 15
    public function getSettings()
376
    {
377 15
        $settings = array();
378 15
        $settings['page'] = $this->getPage();
379 15
        $settings['limit'] = $this->getLimit();
380 15
        $settings['orders'] = $this->getOrders();
381 15
        $settings['filters'] = $this->getFilters();
382 15
        return $settings;
383
    }
384
385
    /**
386
     * Setup params
387
     *
388
     * @param  $params
389
     * @return void
390
     */
391
    public function setParams($params)
392
    {
393
        $this->params = $params;
394
    }
395
396
    /**
397
     * Return params prepared for url builder
398
     *
399
     * @param  array $rewrite
400
     * @return array
401
     */
402 11
    public function getParams(array $rewrite = [])
403
    {
404 11
        $params = $this->params;
405
406
        // change page
407 11
        if (isset($rewrite['page']) && $rewrite['page'] > 1) {
408 4
            $params[$this->prefix . 'page'] = $rewrite['page'];
409
        }
410
411
        // change limit
412 11
        if (isset($rewrite['limit'])) {
413 1
            if ($rewrite['limit'] != $this->defaultLimit) {
414 1
                $params[$this->prefix . 'limit'] = ($rewrite['limit'] != $this->limit)
415 1
                    ? $rewrite['limit'] : $this->limit;
416
            }
417
        } else {
418 10
            if ($this->limit != $this->defaultLimit) {
419
                $params[$this->prefix . 'limit'] = $this->limit;
420
            }
421
        }
422
423
        // change orders
424 11
        if (isset($rewrite['orders'])) {
425 2
            $orders = $rewrite['orders'];
426
        } else {
427 9
            $orders = $this->getOrders();
428
        }
429
430 11
        foreach ($orders as $column => $order) {
431 2
            $params[$this->prefix . 'order-' . $column] = $order;
432
        }
433
434
        // change filters
435 11
        if (isset($rewrite['filters'])) {
436 1
            $filters = $rewrite['filters'];
437
        } else {
438 10
            $filters = $this->getFilters();
439
        }
440 11
        foreach ($filters as $column => $columnFilters) {
441 1
            $columnFilter = [];
442 1
            foreach ($columnFilters as $filterName => $filterValue) {
443 1
                if ($filterName == self::FILTER_EQ) {
444
                    $columnFilter[] = $filterValue;
445
                } else {
446 1
                    $columnFilter[] = $filterName . '-' . $filterValue;
447
                }
448
            }
449 1
            $params[$this->prefix . 'filter-' . $column] = join('-', $columnFilter);
450
        }
451
452 11
        return $params;
453
    }
454
455
    /**
456
     * Get Url
457
     *
458
     * @param  array $params
459
     * @return string
460
     */
461 11
    public function getUrl($params)
462
    {
463
        // prepare params
464 11
        $params = $this->getParams($params);
465
466
        // retrieve URL
467 11
        return Router::getUrl(
468 11
            $this->getModule(),
469 11
            $this->getController(),
470
            $params
471
        );
472
    }
473
474
    /**
475
     * Set allow orders
476
     *
477
     * @param  string[] $orders
478
     * @return void
479
     */
480 33
    public function setAllowOrders(array $orders = [])
481
    {
482 33
        $this->allowOrders = $orders;
483 33
    }
484
485
    /**
486
     * Get allow orders
487
     *
488
     * @return array
489
     */
490 2
    public function getAllowOrders()
491
    {
492 2
        return $this->allowOrders;
493
    }
494
495
    /**
496
     * Add order rule
497
     *
498
     * @param  string $column
499
     * @param  string $order
500
     * @return void
501
     * @throws GridException
502
     */
503 8
    public function addOrder($column, $order = Grid::ORDER_ASC)
504
    {
505 8
        if (!in_array($column, $this->allowOrders)) {
506 1
            throw new GridException('Wrong column order');
507
        }
508
509 7
        if (strtolower($order) != Grid::ORDER_ASC
510 7
            && strtolower($order) != Grid::ORDER_DESC
511
        ) {
512 1
            throw new GridException('Order for column "' . $column . '" is incorrect');
513
        }
514 6
        $column = $this->applyAlias($column);
515
516 6
        $this->orders[$column] = $order;
517 6
    }
518
519
    /**
520
     * Add order rules
521
     *
522
     * @param  array $orders
523
     * @return void
524
     */
525 1
    public function addOrders(array $orders)
526
    {
527 1
        foreach ($orders as $column => $order) {
528 1
            $this->addOrder($column, $order);
529
        }
530 1
    }
531
532
    /**
533
     * Set order
534
     *
535
     * @param  string $column
536
     * @param  string $order  ASC or DESC
537
     * @return void
538
     */
539 6
    public function setOrder($column, $order = Grid::ORDER_ASC)
540
    {
541 6
        $this->orders = [];
542 6
        $this->addOrder($column, $order);
543 4
    }
544
545
    /**
546
     * Set orders
547
     *
548
     * @param  array $orders
549
     * @return void
550
     */
551 1
    public function setOrders(array $orders)
552
    {
553 1
        $this->orders = [];
554 1
        $this->addOrders($orders);
555 1
    }
556
557
    /**
558
     * Get orders
559
     *
560
     * @return array
561
     */
562 24
    public function getOrders()
563
    {
564 24
        $default = $this->getDefaultOrder();
565
566
        // remove default order when another one is set
567 24
        if (is_array($default)
568 24
            && count($this->orders) > 1
569 24
            && isset($this->orders[key($default)])
570 24
            && $this->orders[key($default)] == reset($default)
571
        ) {
572
            unset($this->orders[key($default)]);
573
        }
574
575 24
        return $this->orders;
576
    }
577
578
    /**
579
     * Set allowed filters
580
     *
581
     * @param  string[] $filters
582
     * @return void
583
     */
584 33
    public function setAllowFilters(array $filters = array())
585
    {
586 33
        $this->allowFilters = $filters;
587 33
    }
588
589
    /**
590
     * Get allow filters
591
     *
592
     * @return array
593
     */
594 3
    public function getAllowFilters()
595
    {
596 3
        return $this->allowFilters;
597
    }
598
599
    /**
600
     * Check filter
601
     *
602
     * @param  string $filter
603
     * @return bool
604
     */
605 10
    public function checkFilter($filter)
606
    {
607 10
        if ($filter == self::FILTER_EQ ||
608 9
            $filter == self::FILTER_NE ||
609 6
            $filter == self::FILTER_GT ||
610 6
            $filter == self::FILTER_GE ||
611 6
            $filter == self::FILTER_LT ||
612 6
            $filter == self::FILTER_LE ||
613 5
            $filter == self::FILTER_NUM ||
614 5
            $filter == self::FILTER_ENUM ||
615 10
            $filter == self::FILTER_LIKE
616
        ) {
617 8
            return true;
618
        }
619 2
        return false;
620
    }
621
622
    /**
623
     * Add filter
624
     *
625
     * @param  string $column
626
     * @param  string $filter
627
     * @param  string $value
628
     * @return void
629
     * @throws GridException
630
     */
631 10
    public function addFilter($column, $filter, $value)
632
    {
633 10
        if (!in_array($column, $this->allowFilters) &&
634 10
            !array_key_exists($column, $this->allowFilters)
635
        ) {
636 1
            throw new GridException('Wrong column name for filter');
637
        }
638
639 9
        $filter = strtolower($filter);
640
641 9
        if (!$this->checkFilter($filter)) {
642 1
            throw new GridException('Wrong filter name');
643
        }
644
645 8
        $column = $this->applyAlias($column);
646
        
647 8
        if (!isset($this->filters[$column])) {
648 8
            $this->filters[$column] = [];
649
        }
650 8
        $this->filters[$column][$filter] = $value;
651 8
    }
652
653
654
    /**
655
     * Get filter
656
     *
657
     * @param  string $column
658
     * @param  string $filter
659
     * @return mixed
660
     */
661
    public function getFilter($column, $filter = null)
662
    {
663
        if (isset($this->filters[$column])) {
664
            if ($filter) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filter of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
665
                if (isset($this->filters[$column][$filter])) {
666
                    return $this->filters[$column][$filter];
667
                } else {
668
                    return null;
669
                }
670
            } else {
671
                return $this->filters[$column];
672
            }
673
        } else {
674
            return null;
675
        }
676
    }
677
678
    /**
679
     * Get filters
680
     *
681
     * @return array
682
     */
683 23
    public function getFilters()
684
    {
685 23
        return $this->filters;
686
    }
687
688
    /**
689
     * Add alias
690
     *
691
     * @param  string $key
692
     * @param  string $value
693
     * @return void
694
     */
695
    public function addAlias($key, $value)
696
    {
697
        $this->aliases[$key] = $value;
698
    }
699
700
    /**
701
     * Set aliases
702
     *
703
     * @param array $aliases
704
     * @return void
705
     */
706
    public function setAliases($aliases)
707
    {
708
        $this->aliases = $aliases;
709
    }
710
711
    /**
712
     * Apply Alias
713
     *
714
     * @param  string $key
715
     * @return string
716
     */
717 11
    protected function applyAlias($key)
718
    {
719 11
        return isset($this->aliases[$key])?$this->aliases[$key]:$key;
720
    }
721
722
    /**
723
     * Set page
724
     *
725
     * @param  integer $page
726
     * @return void
727
     * @throws GridException
728
     */
729 33
    public function setPage($page = 1)
730
    {
731 33
        if ($page < 1) {
732 1
            throw new GridException('Wrong page number, should be greater than zero');
733
        }
734 33
        $this->page = (int)$page;
735 33
    }
736
737
    /**
738
     * Get page
739
     *
740
     * @return integer
741
     */
742 17
    public function getPage()
743
    {
744 17
        return $this->page;
745
    }
746
747
    /**
748
     * Set limit per page
749
     *
750
     * @param  integer $limit
751
     * @return void
752
     * @throws GridException
753
     */
754 33
    public function setLimit($limit)
755
    {
756 33
        if ($limit < 1) {
757 1
            throw new GridException('Wrong limit value, should be greater than zero');
758
        }
759 33
        $this->limit = (int)$limit;
760 33
    }
761
762
    /**
763
     * Get limit per page
764
     *
765
     * @return integer
766
     */
767 16
    public function getLimit()
768
    {
769 16
        return $this->limit;
770
    }
771
772
    /**
773
     * Set default limit
774
     *
775
     * @param  integer $limit
776
     * @return void
777
     * @throws GridException
778
     */
779 33
    public function setDefaultLimit($limit)
780
    {
781 33
        if ($limit < 1) {
782 1
            throw new GridException('Wrong default limit value, should be greater than zero');
783
        }
784 33
        $this->setLimit($limit);
785
786 33
        $this->defaultLimit = (int)$limit;
787 33
    }
788
789
    /**
790
     * Get default limit
791
     *
792
     * @return integer
793
     */
794 1
    public function getDefaultLimit()
795
    {
796 1
        return $this->defaultLimit;
797
    }
798
799
    /**
800
     * Set default order
801
     *
802
     * @param  string $column
803
     * @param  string $order ASC or DESC
804
     * @return void
805
     * @throws GridException
806
     */
807 5
    public function setDefaultOrder($column, $order = Grid::ORDER_ASC)
808
    {
809 5
        $this->setOrder($column, $order);
810
811 3
        $this->defaultOrder = array($column => $order);
812 3
    }
813
814
    /**
815
     * Get default order
816
     *
817
     * @return array
818
     */
819 23
    public function getDefaultOrder()
820
    {
821 23
        return $this->defaultOrder;
822
    }
823
}
824