Completed
Pull Request — master (#240)
by Tomáš
06:38
created

ArraySource::sort()   C

Complexity

Conditions 7
Paths 14

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7.0061

Importance

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