ComplexSortStrategy   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 84.44%

Importance

Changes 1
Bugs 1 Features 1
Metric Value
wmc 19
c 1
b 1
f 1
lcom 1
cbo 3
dl 0
loc 112
ccs 38
cts 45
cp 0.8444
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A sortBy() 0 10 3
B createSortTransformFunction() 0 28 3
C getPropertyExtractor() 0 22 9
A sort() 0 8 2
1
<?php
2
3
/**
4
 * Copyright (c) 2013 Jacek Kobus <[email protected]>
5
 * See the file LICENSE.txt for copying permission.
6
 */
7
 
8
namespace PHPExtra\Sorter\Strategy;
9
10
use PHPExtra\Sorter\Comparator\ComparatorInterface;
11
use PHPExtra\Sorter\Comparator\UnicodeCIComparator;
12
13
/**
14
 * The ComplexSortStrategy class
15
 *
16
 * @author Jacek Kobus <[email protected]>
17
 */
18
class ComplexSortStrategy extends AbstractSortStrategy implements StrategyInterface
19
{
20
    /**
21
     * @var array
22
     */
23
    private $propertyMap = array();
24
25
    /**
26
     * @param ComparatorInterface $comparator
27
     */
28 3
    function __construct(ComparatorInterface $comparator = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
29
    {
30 3
        if(!$comparator){
31 3
            $comparator = new UnicodeCIComparator();
32 3
        }
33 3
        parent::__construct($comparator);
34 3
    }
35
36
    /**
37
     * @param mixed               $accessor   Closure or object property name that returns value supported by comparator
38
     * @param int                 $order      Sort sortOrder
39
     * @param ComparatorInterface $comparator Exclusive comparator for given property
40
     *
41
     * @return $this
42
     */
43 1
    public function sortBy($accessor, $order = null, ComparatorInterface $comparator = null)
44
    {
45 1
        $this->propertyMap[] = array(
46 1
            'accessor' => $accessor,
47 1
            'direction' => $order === null ? $this->getSortOrder() : $order,
48 1
            'comparator' => $comparator === null ? $this->getComparator() : $comparator
49 1
        );
50
51 1
        return $this;
52
    }
53
54
    /**
55
     * Create a callback used in usort
56
     *
57
     * @return \Closure
58
     */
59 1
    protected function createSortTransformFunction()
60
    {
61 1
        $propertyMap = $this->propertyMap;
62 1
        $propertyExtractor = $this->getPropertyExtractor();
63 1
        $valueChecker = $this->getValueChecker();
64
65
        return function($a, $b) use ($propertyMap, $propertyExtractor, $valueChecker){
66
67 1
            foreach($propertyMap as $property){
68
69 1
                $valueA = $propertyExtractor($a, $property['accessor']);
70 1
                $valueB = $propertyExtractor($b, $property['accessor']);
71
72 1
                $cmp = $property['comparator'];
73
                /** @var $cmp ComparatorInterface */
74
75 1
                $valueChecker($valueA, $valueB, $cmp);
76 1
                $result = $cmp->compare($valueA, $valueB);
77
78 1
                if($result != 0){
79 1
                    return $result * $property['direction'];
80
                }
81 1
            }
82
83
            return 0;
84
85 1
        };
86
    }
87
88
    /**
89
     * Get callable used for extracting values from sortable entities (objects, arrays etc.)
90
     * This method extracts value from k, where k is an element of collection(i => k).
91
     * Accessor can be customized to add sorting ability to a complex objects.
92
     *
93
     * @return \Closure Takes two arguments, $property and $accessor
94
     */
95
    private function getPropertyExtractor()
96
    {
97 1
        return function($property, $accessor = null){
98
99 1
            if(is_string($property) || !$accessor){
100
                return $property;
101
            }
102
103 1
            if($accessor instanceof \Closure){
104
                return $accessor($property);
105 1
            }elseif(is_string($accessor)){
106
107 1
                if(is_array($property) || $property instanceof \ArrayAccess){
108
                    return $property[$accessor];
109 1
                }elseif(is_object($property) && property_exists($property, $accessor)){
110 1
                    return $property->$accessor;
111
                }
112
            }
113
114
            throw new \RuntimeException(sprintf('Unable to resolve property value: %s', gettype($property)));
115 1
        };
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 1
    public function sort(array $collection)
122
    {
123 1
        if(empty($this->propertyMap)){
124
            throw new \RuntimeException(sprintf('Missing sort properties - add them using sortBy(...)'));
125
        }
126
127 1
        return parent::sort($collection);
128
    }
129
}