ComplexSortStrategy::sort()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 9.4286
cc 2
eloc 4
nc 2
nop 1
crap 2.0625
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
}