Completed
Push — master ( 369b2c...ffb215 )
by Anton
8s
created

Rest::methodPost()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 29
ccs 15
cts 15
cp 1
rs 8.439
cc 5
eloc 17
nc 7
nop 0
crap 5
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link https://github.com/bluzphp/framework
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Bluz\Controller;
13
14
use Bluz\Application\Exception\BadRequestException;
15
use Bluz\Application\Exception\NotFoundException;
16
use Bluz\Application\Exception\NotImplementedException;
17
use Bluz\Proxy\Response;
18
use Bluz\Proxy\Request;
19
use Bluz\Proxy\Router;
20
use Bluz\Validator\Exception\ValidatorException;
21
22
/**
23
 * Controller
24
 *
25
 * @package  Bluz\Controller
26
 * @author   Anton Shevchuk
27
 * @link     https://github.com/bluzphp/framework/wiki/Controller-Rest
28
 */
29
class Rest extends AbstractController
30
{
31
    /**
32
     * @var string relation list
33
     */
34
    protected $relation;
35
36
    /**
37
     * @var string relation Id
38
     */
39
    protected $relationId;
40
41
    /**
42
     * @var array params of query
43
     */
44
    protected $params = array();
45
46
    /**
47
     * @var array query data
48
     */
49
    protected $data = array();
50
51
    /**
52
     * Prepare request for processing
53
     */
54 25
    public function __construct()
55
    {
56 25
        parent::__construct();
57
58
        // get path
59
        // %module% / %controller% / %id% / %relation% / %id%
60 25
        $path = Router::getCleanUri();
61
62 25
        $params = explode('/', rtrim($path, '/'));
63
64
        // module
65 25
        array_shift($params);
66
67
        // controller
68 25
        array_shift($params);
69
70 25
        if (sizeof($params)) {
71 13
            $this->primary = explode('-', array_shift($params));
72
        }
73 25
        if (sizeof($params)) {
74 2
            $this->relation = array_shift($params);
75
        }
76 25
        if (sizeof($params)) {
77 1
            $this->relationId = array_shift($params);
78
        }
79 25
    }
80
81
    /**
82
     * {@inheritdoc}
83
     *
84
     * Everyone method can return:
85
     *    401 Unauthorized - if authorization is required
86
     *    403 Forbidden - if user don't have permissions
87
     *    501 Not Implemented - if something not exists
88
     *
89
     * Methods can return:
90
     *    HEAD   /module/rest/   -> 200 // return overview of collection
91
     *    HEAD   /module/rest/id -> 200 // return overview of item
92
     *                           -> 404 // not found
93
     *    GET    /module/rest/   -> 200 // return collection or
94
     *                           -> 206 // return part of collection
95
     *    GET    /module/rest/id -> 200 // return one item or
96
     *                           -> 404 // not found
97
     *    POST   /module/rest/   -> 201 // item created or
98
     *                           -> 400 // bad request, validation error
99
     *    POST   /module/rest/id -> 501 // error, not used in REST
100
     *    PATCH  /module/rest/
101
     *    PUT    /module/rest/   -> 200 // all items was updated or
102
     *                           -> 207 // multi-status ?
103
     *    PATCH  /module/rest/id
104
     *    PUT    /module/rest/id -> 200 // item was updated or
105
     *                           -> 304 // item not modified or
106
     *                           -> 400 // bad request, validation error or
107
     *                           -> 404 // not found
108
     *    DELETE /module/rest/   -> 204 // all items was deleted or
109
     *                           -> 207 // multi-status ?
110
     *    DELETE /module/rest/id -> 204 // item was deleted
111
     *                           -> 404 // not found
112
     *
113
     * @return mixed
114
     * @throws NotImplementedException
115
     * @throws NotFoundException
116
     * @throws BadRequestException
117
     */
118 25
    public function __invoke()
119
    {
120 25
        switch ($this->method) {
121 25
            case Request::METHOD_HEAD:
122 22
            case Request::METHOD_GET:
123 8
                return $this->methodGet();
124
                // break
125 17
            case Request::METHOD_POST:
126 4
                return $this->methodPost();
127
                // break
128 13
            case Request::METHOD_PATCH:
129 13
            case Request::METHOD_PUT:
130 6
                return $this->methodPut();
131
                // break
132 7
            case Request::METHOD_DELETE:
133 4
                return $this->methodDelete();
134
                // break
135 3
            case Request::METHOD_OPTIONS:
136 2
                return $this->methodOptions();
137
                // break
138
            default:
139 1
                throw new NotImplementedException();
140
        }
141
    }
142
143
    /**
144
     * Method HEAD and GET
145
     *
146
     * @return mixed
147
     */
148 8
    public function methodGet()
149
    {
150 8
        if (!empty($this->primary)) {
151
            // @throws NotFoundException
152 4
            $result = $this->readOne($this->primary);
153 3
            return [$result];
154
        } else {
155
            // setup default offset and limit - safe way
156 4
            $offset = isset($this->params['offset'])?$this->params['offset']:0;
157 4
            $limit = isset($this->params['limit'])?$this->params['limit']:10;
158
159 4
            if ($range = Request::getHeader('Range')) {
160 1
                list(, $offset, $last) = preg_split('/[-=]/', $range);
161
                // for better compatibility
162 1
                $limit = $last - $offset;
163
            }
164 4
            return $this->readSet($offset, $limit, $this->params);
165
        }
166
    }
167
168
    /**
169
     * Method POST
170
     *
171
     * @return array|false
172
     * @throws BadRequestException
173
     * @throws NotImplementedException
174
     */
175 4
    public function methodPost()
176
    {
177 4
        if (!empty($this->primary)) {
178
            // POST + ID is incorrect behaviour
179 1
            throw new NotImplementedException();
180
        }
181
182
        try {
183 3
            $result = $this->createOne($this->data);
184 1
            if (!$result) {
185
                // system can't create record with this data
186
                throw new BadRequestException();
187
            }
188
189 1
            if (is_array($result)) {
190 1
                $result = join('-', array_values($result));
191
            }
192 2
        } catch (ValidatorException $e) {
193 2
            Response::setStatusCode(400);
194 2
            return ['errors' => $e->getErrors()];
195
        }
196
197 1
        Response::setStatusCode(201);
198 1
        Response::setHeader(
199 1
            'Location',
200 1
            Router::getUrl(Request::getModule(), Request::getController()).'/'.$result
201
        );
202 1
        return false; // disable view
203
    }
204
205
    /**
206
     * Method PUT
207
     *
208
     * @return array|false
209
     * @throws BadRequestException
210
     */
211 6
    public function methodPut()
212
    {
213 6
        if (!sizeof($this->data)) {
214
            // data not found
215 1
            throw new BadRequestException();
216
        }
217
218
        try {
219 5
            if (!empty($this->primary)) {
220
                // update one item
221 4
                $result = $this->updateOne($this->primary, $this->data);
222
            } else {
223
                // update collection
224 1
                $result = $this->updateSet($this->data);
225
            }
226
            // if $result === 0 it's means a update is not apply
227
            // or records not found
228 2
            if (0 === $result) {
229 2
                Response::setStatusCode(304);
230
            }
231 3
        } catch (ValidatorException $e) {
232 1
            Response::setStatusCode(400);
233 1
            return ['errors' => $e->getErrors()];
234
        }
235 2
        return false; // disable view
236
    }
237
238
    /**
239
     * Method DELETE
240
     *
241
     * @return false
242
     * @throws BadRequestException
243
     */
244 4
    public function methodDelete()
245
    {
246 4
        if (!empty($this->primary)) {
247
            // delete one
248
            // @throws NotFoundException
249 2
            $this->deleteOne($this->primary);
250
        } else {
251
            // delete collection
252
            // @throws NotFoundException
253 2
            if (!sizeof($this->data)) {
254
                // data not exist
255 1
                throw new BadRequestException();
256
            }
257 1
            $this->deleteSet($this->data);
258
        }
259 1
        Response::setStatusCode(204);
260 1
        return false; // disable view
261
    }
262
263
    /**
264
     * Method OPTIONS
265
     *
266
     * @return false
267
     */
268 2
    public function methodOptions()
269
    {
270 2
        $allow = $this->getMethods(sizeof($this->primary));
271 2
        Response::setHeader('Allow', join(',', $allow));
272 2
        return false; // no body
273
    }
274
275
    /**
276
     * Get allowed methods by CRUD
277
     *
278
     * @param  bool $primary
279
     * @return array
280
     */
281 2
    protected function getMethods($primary = false)
282
    {
283 2
        $methods = $this->getCrud()->getMethods();
284
285 2
        $allow = [Request::METHOD_HEAD, Request::METHOD_OPTIONS];
286
287 2
        if ($primary) {
288 1
            if (in_array('readOne', $methods)) {
289 1
                $allow[] = Request::METHOD_GET;
290
            }
291 1
            if (in_array('updateOne', $methods)) {
292 1
                $allow[] = Request::METHOD_PATCH;
293 1
                $allow[] = Request::METHOD_PUT;
294
            }
295 1
            if (in_array('deleteOne', $methods)) {
296 1
                $allow[] = Request::METHOD_DELETE;
297
            }
298
        } else {
299 1
            if (in_array('readSet', $methods)) {
300 1
                $allow[] = Request::METHOD_GET;
301
            }
302 1
            if (in_array('createOne', $methods)
303 1
                || in_array('createSet', $methods)) {
304 1
                $allow[] = Request::METHOD_POST;
305
            }
306 1
            if (in_array('updateSet', $methods)) {
307
                $allow[] = Request::METHOD_PATCH;
308
                $allow[] = Request::METHOD_PUT;
309
            }
310 1
            if (in_array('deleteSet', $methods)) {
311
                $allow[] = Request::METHOD_DELETE;
312
            }
313
        }
314 2
        return $allow;
315
    }
316
}
317