Completed
Push — master ( 53caa0...17e7d7 )
by Phil
02:05
created

QueryStringParserTrait::parseInclude()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 25
cts 25
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 22
nc 4
nop 1
crap 6
1
<?php
2
3
namespace Percy\Http;
4
5
use InvalidArgumentException;
6
7
trait QueryStringParserTrait
8
{
9
    /**
10
     * @var integer
11
     */
12
    protected $param = 0;
13
14
    /**
15
     * Parse HTTP query string and return array representation
16
     * to be attached to a database query.
17
     *
18
     * @param string $query
19
     *
20
     * @return array
21
     */
22 20
    public function parseQueryString($query)
23
    {
24 20
        if (empty($query)) {
25 3
            return [];
26
        }
27
28 18
        parse_str($query, $split);
29
30 18
        $query = [];
31
32 18
        while (list($key, $value) = each($split)) {
33 18
            $mapped = call_user_func_array([$this, 'filterQueryParams'], [$key, $value]);
34 14
            if ($mapped !== false) {
35 13
                $query[$key] = $mapped;
36 13
            }
37 14
        }
38
39 14
        return $query;
40
    }
41
42
    /**
43
     * Map the parsed query string in to correct array structure.
44
     *
45
     * @param string $key
46
     * @param mixed  $value
47
     *
48
     * @return array|boolean
49
     */
50 18
    protected function filterQueryParams($key, $value)
51
    {
52
        switch ($key) {
53 18
            case 'limit':
54 18
            case 'offset':
55 1
                return (int) $value;
56 18
            case 'sort':
57 6
                return $this->parseSort($value);
58 16
            case 'filter':
59 10
                return $this->parseFilters((array) $value);
60 8
            case 'has':
61 2
                return explode(',', $value);
62 7
            case 'include':
63 2
                return $this->parseInclude(explode(',', $value));
64 5
            case 'search':
65 4
                return $this->parseSearch($value);
66 4
            case 'minscore':
67 3
                return (float) $value;
68 1
            default:
69 1
                return false;
70 1
        }
71
    }
72
73
    /**
74
     * Map sorts to a usable format.
75
     *
76
     * @param string $value
77
     *
78
     * @return array
79
     */
80 6
    protected function parseSort($value)
81
    {
82 6
        $map   = [];
83 6
        $sorts = explode(',', $value);
84
85 6
        foreach ($sorts as $sort) {
86 6
            $sort      = explode('|', $sort);
87 6
            $direction = (count($sort) > 1) ? $sort[1] : 'asc';
88
89 6
            if (in_array($sort[0], ['rand', 'random'])) {
90 2
                return 'RAND()';
91
            }
92
93 5
            $map[] = [
94 5
                'field'     => $sort[0],
95
                'direction' => $direction
96 5
            ];
97 5
        }
98
99 5
        return $map;
100
    }
101
102
    /**
103
     * Map search to a usable format.
104
     *
105
     * @param string $value
106
     *
107
     * @return array
108
     */
109 4
    protected function parseSearch($value)
110
    {
111 4
        $search = explode('|', $value);
112
113 4
        if (count($search) !== 2) {
114 1
            throw new InvalidArgumentException(
115
                'Malformed query string, search format should be (search=field|term) or (search=field1,field2|term)'
116 1
            );
117
        }
118
119
        return [
120 3
            'fields' => $search[0],
121 3
            'term'   => $search[1]
122 3
        ];
123
    }
124
125
    /**
126
     * Map filters in to useable array.
127
     *
128
     * @param array $filters
129
     *
130
     * @return array
131
     */
132 10
    protected function parseFilters(array $filters)
133
    {
134 10
        $mapped = [];
135
136 10
        $this->param = 0;
137
138 10
        foreach ($filters as $filter) {
139 10
            $mapped[] = $this->parseFilter($filter);
140 8
        }
141
142 8
        return $mapped;
143
    }
144
145
    /**
146
     * Parse an individual filter.
147
     *
148
     * @param string $filter
149
     *
150
     * @return array
151
     */
152 11
    protected function parseFilter($filter)
153
    {
154 11
        $filter = explode('|', $filter);
155
156 11
        if (count($filter) !== 3) {
157 1
            throw new InvalidArgumentException(
158
                'Malformed query string, filter format should be (filter[]=field|delimiter|value)'
159 1
            );
160
        }
161
162 10
        $filter = array_combine(['field', 'delimiter', 'value'], $filter);
163
164 10
        $filter['binding']   = str_replace('.', '_', $filter['field']) . '_' . $this->param++;
165 10
        $filter['delimiter'] = strtolower($filter['delimiter']);
166 10
        $filter['delimiter'] = html_entity_decode($filter['delimiter']);
167
168 10
        if (! in_array($filter['delimiter'], [
169 10
            '=', '!=', '<>', '<=', '>=', '<', '>', 'in', 'not in', 'like', 'not like'
170 10
        ])) {
171 2
            throw new InvalidArgumentException(sprintf('(%s) is not an accepted delimiter', $filter['delimiter']));
172
        }
173
174 9
        return $filter;
175
    }
176
177
    /**
178
     * Map includes in to useable array.
179
     *
180
     * @param array $includes
181
     *
182
     * @return array
183
     */
184 2
    protected function parseInclude(array $includes)
185
    {
186 2
        $mapped = [];
187
188 2
        foreach ($includes as $include) {
189 2
            $parts = explode(';', $include);
190 2
            $key   = array_shift($parts);
191
192 2
            if (empty($parts)) {
193 2
                $mapped[$key] = [];
194 2
                continue;
195
            }
196
197 2
            $mapped[$key]['filter'] = [];
198 2
            $mapped[$key]['limit']  = null;
199
200 2
            foreach ($parts as $part) {
201 2
                $split = explode('|', $part);
202
203 2
                switch (count($split)) {
204 2
                    case 2:
205 1
                        $mapped[$key]['limit'] = $split[1];
206 1
                    break;
207 2
                    case 3:
208 1
                        $mapped[$key]['filter'][] = $this->parseFilter($part);
209 1
                    break;
210 1
                    default:
211 1
                        throw new InvalidArgumentException('Include formatted incorrectly.');
212 2
                }
213 1
            }
214 1
        }
215
216 1
        return $mapped;
217
    }
218
}
219