ArraySource::sort()   B
last analyzed

Complexity

Conditions 7
Paths 14

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7.0061

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 19
cts 20
cp 0.95
rs 8.5546
c 0
b 0
f 0
cc 7
nc 14
nop 1
crap 7.0061
1
<?php
2
3
/**
4
 * This file is part of the Grido (https://github.com/o5/grido)
5
 *
6
 * Copyright (c) 2011 Petr Bugyík (http://petr.bugyik.cz)
7
 *
8
 * For the full copyright and license information, please view
9
 * the file LICENSE.md that was distributed with this source code.
10
 */
11
12
namespace Grido\DataSources;
13
14
use Grido\Exception;
15
use Grido\Components\Filters\Condition;
16
17
use Nette\Utils\Strings;
18
19
/**
20
 * Array data source.
21
 *
22
 * @package     Grido
23
 * @subpackage  DataSources
24
 * @author      Josef Kříž <[email protected]>
25
 * @author      Petr Bugyík
26
 *
27
 * @property-read array $data
28
 * @property-read int $count
29
 */
30 1
class ArraySource implements IDataSource
31
{
32
    use \Nette\SmartObject;
33
34
    /** @var array */
35
    protected $data;
36
37
    /**
38
     * @param array $data
39
     */
40 1
    public function __construct(array $data)
41 1
    {
42
        $this->data = $data;
43
    }
44
45
    /**
46
     * This method needs tests!
47
     * @param Condition $condition
48
     * @param array $data
49
     * @return array
50
     */
51
    protected function makeWhere(Condition $condition, array $data = NULL)
52 1
    {
53 1
        $data = $data === NULL
54
            ? $this->data
55 1
            : $data;
56 1
57 1
        return array_filter($data, function ($row) use ($condition) {
58
            if ($condition->callback) {
59 1
                return call_user_func_array($condition->callback, [$condition->value, $row]);
60 1
            }
61 1
62 1
            $i = 0;
63 1
            $results = [];
64 1
            foreach ($condition->column as $column) {
65
                if (Condition::isOperator($column)) {
66 1
                    $results[] = " $column ";
67 1
68 1
                } else {
69 1
                    $i = count($condition->condition) > 1 ? $i : 0;
70 1
                    $results[] = (int) $this->compare(
71 1
                        $row[$column],
72 1
                        $condition->condition[$i],
73
                        isset($condition->value[$i]) ? $condition->value[$i] : NULL
74 1
                    );
75
76 1
                    $i++;
77
                }
78 1
            }
79 1
80 1
            $result = implode('', $results);
81 1
            return count($condition->column) === 1
82 1
                ? (bool) $result
83
                : eval("return $result;"); // QUESTION: How to remove this eval? hmmm?
84
        });
85
    }
86
87
    /**
88
     * @param string $actual
89
     * @param string $condition
90
     * @param mixed $expected
91
     * @throws Exception
92
     * @return bool
93
     */
94 1
    public function compare($actual, $condition, $expected)
95 1
    {
96 1
        $expected = (array) $expected;
97
        $expected = current($expected);
98 1
        $cond = str_replace(' ?', '', $condition);
99 1
100 1
        if ($cond === 'LIKE') {
101
            $actual = Strings::toAscii($actual);
102 1
            $expected = Strings::toAscii($expected);
103 1
104
            $pattern = str_replace('%', '(.|\s)*', preg_quote($expected, '/'));
105 1
            return (bool) preg_match("/^{$pattern}$/i", $actual);
106 1
107
        } elseif ($cond === '=') {
108 1
            return $actual == $expected;
109 1
110
        } elseif ($cond === '<>') {
111 1
            return $actual != $expected;
112 1
113
        } elseif ($cond === 'IS NULL') {
114 1
            return $actual === NULL;
115 1
116
        } elseif ($cond === 'IS NOT NULL') {
117 1
            return $actual !== NULL;
118 1
119
        } elseif ($cond === '<') {
120 1
            return (int) $actual < $expected;
121 1
122
        } elseif ($cond === '<=') {
123 1
            return (int) $actual <= $expected;
124 1
125
        } elseif ($cond === '>') {
126 1
            return (int) $actual > $expected;
127 1
128
        } elseif ($cond === '>=') {
129
            return (int) $actual >= $expected;
130 1
131
        } else {
132
            throw new Exception("Condition '$condition' is not implemented yet.");
133
        }
134
    }
135
136
    /*********************************** interface IDataSource ************************************/
137
138
    /**
139
     * @return int
140
     */
141 1
    public function getCount()
142
    {
143
        return count($this->data);
144
    }
145
146
    /**
147
     * @return array
148
     */
149 1
    public function getData()
150
    {
151
        return $this->data;
152
    }
153
154
    /**
155
     * @param array $conditions
156
     */
157 1
    public function filter(array $conditions)
158 1
    {
159 1
        foreach ($conditions as $condition) {
160 1
            $this->data = $this->makeWhere($condition);
161
        }
162
    }
163
164
    /**
165
     * @param int $offset
166
     * @param int $limit
167
     */
168 1
    public function limit($offset, $limit)
169 1
    {
170
        $this->data = array_slice($this->data, $offset, $limit);
171
    }
172
173
    /**
174
     * @param array $sorting
175
     * @throws Exception
176
     */
177 1
    public function sort(array $sorting)
178
    {
179
        if (count($sorting) > 1) {
180
            throw new Exception('Multi-column sorting is not implemented yet.');
181 1
        }
182 1
183 1
        foreach ($sorting as $column => $sort) {
184 1
            $data = [];
185 1
            foreach ($this->data as $item) {
186 1
                $sorter = (string) $item[$column];
187
                $data[$sorter][] = $item;
188 1
            }
189 1
190 1
            if ($sort === 'ASC') {
191 1
                ksort($data);
192
            } else {
193
                krsort($data);
194 1
            }
195 1
196 1
            $this->data = [];
197 1
            foreach ($data as $i) {
198 1
                foreach ($i as $item) {
199 1
                    $this->data[] = $item;
200 1
                }
201 1
            }
202
        }
203
    }
204
205
    /**
206
     * @param mixed $column
207
     * @param array $conditions
208
     * @param int $limit
209
     * @return array
210
     * @throws Exception
211
     */
212 1
    public function suggest($column, array $conditions, $limit)
213 1
    {
214 1
        $data = $this->data;
215 1
        foreach ($conditions as $condition) {
216
            $data = $this->makeWhere($condition, $data);
217 1
        }
218
219 1
        array_slice($data, 1, $limit);
220 1
221 1
        $items = [];
222 1
        foreach ($data as $row) {
223 1
            if (is_string($column)) {
224
                $value = (string) $row[$column];
225
            } elseif (is_callable($column)) {
226
                $value = (string) $column($row);
227
            } else {
228
                $type = gettype($column);
229
                throw new Exception("Column of suggestion must be string or callback, $type given.");
230 1
            }
231 1
232
            $items[$value] = \Latte\Runtime\Filters::escapeHtml($value);
233 1
        }
234 1
235
        sort($items);
236
        return array_values($items);
237 1
    }
238
}
239