Completed
Branch master (6d49ce)
by
unknown
07:31
created

QueryParametersParser::getPagingParameters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php namespace Neomerx\JsonApi\Http\Query;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use \Psr\Log\LoggerAwareTrait;
20
use \Psr\Log\LoggerAwareInterface;
21
use \Neomerx\JsonApi\Document\Error;
22
use \Neomerx\JsonApi\I18n\Translator as T;
23
use \Psr\Http\Message\ServerRequestInterface;
24
use \Neomerx\JsonApi\Exceptions\JsonApiException as E;
25
use \Neomerx\JsonApi\Contracts\Http\HttpFactoryInterface;
26
use \Neomerx\JsonApi\Contracts\Encoder\Parameters\SortParameterInterface;
27
use \Neomerx\JsonApi\Contracts\Http\Query\QueryParametersParserInterface;
28
29
/**
30
 * @package Neomerx\JsonApi
31
 */
32
class QueryParametersParser implements QueryParametersParserInterface, LoggerAwareInterface
33
{
34
    use LoggerAwareTrait;
35
36
    /**
37
     * @var HttpFactoryInterface
38
     */
39
    private $factory;
40
41
    /**
42
     * @param HttpFactoryInterface $factory
43
     */
44 27
    public function __construct(HttpFactoryInterface $factory)
45
    {
46 27
        $this->factory = $factory;
47 27
    }
48
49
    /**
50
     * @inheritdoc
51
     */
52 26
    public function parse(ServerRequestInterface $request)
53
    {
54 26
        $parameters = $request->getQueryParams();
55
56 26
        return $this->factory->createQueryParameters(
57 26
            $this->getIncludePaths($parameters),
58 25
            $this->getFieldSets($parameters),
59 24
            $this->getSortParameters($parameters),
60 21
            $this->getPagingParameters($parameters),
61 20
            $this->getFilteringParameters($parameters),
62 19
            $this->getUnrecognizedParameters($parameters)
63 19
        );
64
    }
65
66
    /**
67
     * @param array $parameters
68
     *
69
     * @return array|null
70
     */
71 26
    private function getIncludePaths(array $parameters)
72
    {
73 26
        $paths  = $this->getStringParamOrNull($parameters, self::PARAM_INCLUDE);
74 25
        $result = empty($paths) === false ? explode(',', rtrim($paths, ',')) : null;
75
76 25
        return $result;
77
    }
78
79
    /**
80
     * @param array $parameters
81
     *
82
     * @return array|null
83
     *
84
     * @SuppressWarnings(PHPMD.StaticAccess)
85
     * @SuppressWarnings(PHPMD.ElseExpression)
86
     */
87 25
    private function getFieldSets(array $parameters)
88
    {
89 25
        $result    = [];
90 25
        $fieldSets = $this->getParamOrNull($parameters, self::PARAM_FIELDS);
91 25
        if (empty($fieldSets) === false && is_array($fieldSets)) {
92 16
            foreach ($fieldSets as $type => $fields) {
93
                // We expect fields to be comma separated or empty strings. Multi-dimension arrays are not allowed.
94 16
                if (is_string($fields) === false) {
95 1
                    $detail = T::t(self::PARAM_FIELDS . ' parameter values should be comma separated strings.');
96 1
                    throw new E($this->createInvalidQueryErrors($detail), E::HTTP_CODE_BAD_REQUEST);
97
                }
98 15
                $result[$type] = (empty($fields) === true ? [] : explode(',', $fields));
99 15
            }
100 15
        } else {
101 9
            $result = null;
102
        }
103
104 24
        return $result;
105
    }
106
107
    /**
108
     * @param array $parameters
109
     *
110
     * @return SortParameterInterface[]|null
111
     *
112
     * @SuppressWarnings(PHPMD.StaticAccess)
113
     */
114 24
    protected function getSortParameters(array $parameters)
115
    {
116 24
        $sortParams = null;
117 24
        $sortParam  = $this->getStringParamOrNull($parameters, self::PARAM_SORT);
118 23
        if ($sortParam !== null) {
119 17
            foreach (explode(',', $sortParam) as $param) {
120 17
                if (empty($param) === true) {
121 1
                    $detail = T::t('Parameter ' . self::PARAM_SORT . ' should have valid value specified.');
122 1
                    throw new E($this->createInvalidQueryErrors($detail), E::HTTP_CODE_BAD_REQUEST);
123
                }
124 17
                $isDesc = $isDesc = ($param[0] === '-');
125 17
                $sortField = ltrim($param, '+-');
126 17
                if (empty($sortField) === true) {
127 1
                    $detail = T::t('Parameter ' . self::PARAM_SORT . ' should have valid name specified.');
128 1
                    throw new E($this->createInvalidQueryErrors($detail), E::HTTP_CODE_BAD_REQUEST);
129
                }
130 17
                $sortParams[] = $this->factory->createSortParam($sortField, $isDesc === false);
131 17
            }
132 15
        }
133 21
        return $sortParams;
134
    }
135
136
    /**
137
     * @param array $parameters
138
     *
139
     * @return array|null
140
     */
141 21
    private function getPagingParameters(array $parameters)
142
    {
143 21
        return $this->getArrayParamOrNull($parameters, self::PARAM_PAGE);
144
    }
145
146
    /**
147
     * @param array $parameters
148
     *
149
     * @return array|null
150
     */
151 20
    private function getFilteringParameters(array $parameters)
152
    {
153 20
        return $this->getArrayParamOrNull($parameters, self::PARAM_FILTER);
154
    }
155
156
    /**
157
     * @param array $parameters
158
     *
159
     * @return array|null
160
     */
161 19
    private function getUnrecognizedParameters(array $parameters)
162
    {
163
        $supported = [
164 19
            self::PARAM_INCLUDE => 0,
165 19
            self::PARAM_FIELDS  => 0,
166 19
            self::PARAM_PAGE    => 0,
167 19
            self::PARAM_FILTER  => 0,
168 19
            self::PARAM_SORT    => 0,
169 19
        ];
170 19
        $unrecognized = array_diff_key($parameters, $supported);
171 19
        return empty($unrecognized) === true ? null : $unrecognized;
172
    }
173
174
    /**
175
     * @param array $parameters
176
     * @param string $name
177
     *
178
     * @return array|null
179
     *
180
     * @SuppressWarnings(PHPMD.StaticAccess)
181
     */
182 21 View Code Duplication
    private function getArrayParamOrNull(array $parameters, $name)
183
    {
184 21
        $value = $this->getParamOrNull($parameters, $name);
185
186 21
        if ($value !== null && is_array($value) === false) {
187 2
            $detail = T::t('Value should be either an array or null.');
188 2
            throw new E($this->createParamErrors($name, $detail), E::HTTP_CODE_BAD_REQUEST);
189
        }
190
191 20
        return $value;
192
    }
193
194
    /**
195
     * @param array $parameters
196
     * @param string $name
197
     *
198
     * @return string|null
199
     *
200
     * @SuppressWarnings(PHPMD.StaticAccess)
201
     */
202 26 View Code Duplication
    private function getStringParamOrNull(array $parameters, $name)
203
    {
204 26
        $value = $this->getParamOrNull($parameters, $name);
205
206 26
        if ($value !== null && is_string($value) === false) {
207 2
            $detail = T::t('Value should be either a string or null.');
208 2
            throw new E($this->createParamErrors($name, $detail), E::HTTP_CODE_BAD_REQUEST);
209
        }
210
211 25
        return $value;
212
    }
213
214
    /**
215
     * @param array  $parameters
216
     * @param string $name
217
     *
218
     * @return mixed
219
     */
220 26
    private function getParamOrNull(array $parameters, $name)
221
    {
222 26
        return isset($parameters[$name]) === true ? $parameters[$name] : null;
223
    }
224
225
    /**
226
     * @param string $detail
227
     *
228
     * @return Error[]
229
     *
230
     * @SuppressWarnings(PHPMD.StaticAccess)
231
     */
232 3
    protected function createInvalidQueryErrors($detail)
233
    {
234
        // NOTE: external libraries might expect this method to exist and have certain signature
235
        // @see https://github.com/neomerx/json-api/issues/185#issuecomment-329135390
236
237 3
        $title = T::t('Invalid query.');
238
239
        return [
240 3
            new Error(null, null, null, null, $title, $detail),
241 3
        ];
242
    }
243
244
    /**
245
     * @param string $name
246
     * @param string $detail
247
     *
248
     * @return Error[]
249
     *
250
     * @SuppressWarnings(PHPMD.StaticAccess)
251
     */
252 4
    protected function createParamErrors($name, $detail)
253
    {
254
        // NOTE: external libraries might expect this method to exist and have certain signature
255
        // @see https://github.com/neomerx/json-api/issues/185#issuecomment-329135390
256
257 4
        $title  = T::t('Invalid query parameter.');
258
        $source = [
259 4
            Error::SOURCE_PARAMETER => $name,
260 4
        ];
261
262
        return [
263 4
            new Error(null, null, null, null, $title, $detail, $source),
264 4
        ];
265
    }
266
}
267