Test Failed
Pull Request — master (#6)
by
unknown
04:05
created

Rest::restForwarding()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 12
c 2
b 0
f 0
dl 0
loc 22
ccs 0
cts 13
cp 0
rs 8.8333
cc 7
nc 6
nop 1
crap 56
1
<?php
2
/**
3
 * This file is part of the Zemit Framework.
4
 *
5
 * (c) Zemit Team <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE.txt
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Zemit\Mvc\Controller;
12
13
use League\Csv\CharsetConverter;
14
use Phalcon\Http\Response;
15
use Phalcon\Mvc\Dispatcher;
16
use Phalcon\Mvc\Model\Resultset;
17
use League\Csv\Writer;
18
use Phalcon\Version;
19
use Zemit\Db\Profiler;
20
use Zemit\Utils\Slug;
21
22
/**
23
 * Class Rest
24
 *
25
 * @author Julien Turbide <[email protected]>
26
 * @copyright Zemit Team <[email protected]>
27
 *
28
 * @since 1.0
29
 * @version 1.0
30
 *
31
 *
32
 * @property Profiler $profiler
33
 * @package Zemit\Mvc\Controller
34
 */
35
class Rest extends \Zemit\Mvc\Controller
36
{
37
    use Model;
0 ignored issues
show
Bug introduced by
The trait Zemit\Mvc\Controller\Model requires the property $loader which is not provided by Zemit\Mvc\Controller\Rest.
Loading history...
38
    
39
    /**
40
     * Rest Bootstrap
41
     */
42
    public function indexAction($id = null)
43
    {
44
        $this->restForwarding($id);
45
    }
46
    
47
    /**
48
     * Rest bootstrap forwarding
49
     *
50
     * @return \Phalcon\Http\ResponseInterface
51
     */
52
    protected function restForwarding($id = null)
53
    {
54
        $id ??= $this->getParam('id');
55
        
56
        if ($this->request->isPost() || $this->request->isPut()) {
57
            $this->dispatcher->forward(['action' => 'save']);
58
        }
59
        else if ($this->request->isDelete()) {
60
            $this->dispatcher->forward(['action' => 'delete']);
61
        }
62
        else if ($this->request->isGet()) {
63
            if (is_null($id)) {
64
                $this->dispatcher->forward(['action' => 'getList']);
65
            }
66
            else {
67
                $this->dispatcher->forward(['action' => 'get']);
68
            }
69
        }
70
        else if ($this->request->isOptions()) {
71
            
72
            // @TODO handle this correctly
73
            return $this->setRestResponse(['result' => 'OK']);
74
        }
75
    }
76
    
77
    /**
78
     * Retrieving a single record
79
     * Alias of method getAction()
80
     * @deprecated Should use getAction() method instead
81
     *
82
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
83
     *
84
     * @return bool|\Phalcon\Http\ResponseInterface
85
     */
86
    public function getSingleAction($id = null) {
87
        return $this->getAction($id);
88
    }
89
    
90
    /**
91
     * Retrieving a single record
92
     *
93
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
94
     *
95
     * @return bool|\Phalcon\Http\ResponseInterface
96
     */
97
    public function getAction($id = null)
98
    {
99
        $modelName = $this->getModelClassName();
100
        $single = $this->getSingle($id, $modelName, null);
101
        
102
        $this->view->single = $single ? $single->expose($this->getExpose()) : false;
0 ignored issues
show
Bug introduced by
The method expose() does not exist on Phalcon\Mvc\Model\Resultset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

102
        $this->view->single = $single ? $single->/** @scrutinizer ignore-call */ expose($this->getExpose()) : false;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
        $this->view->model = $modelName;
104
        $this->view->source = $single ? $single->getSource() : false;
0 ignored issues
show
Bug introduced by
The method getSource() does not exist on Phalcon\Mvc\Model\Resultset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

104
        $this->view->source = $single ? $single->/** @scrutinizer ignore-call */ getSource() : false;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
105
        
106
        if (!$single) {
107
            $this->response->setStatusCode(404, 'Not Found');
108
            
109
            return false;
110
        }
111
        
112
        return $this->setRestResponse();
113
    }
114
    
115
    /**
116
     * Retrieving a record list
117
     * Alias of method getListAction()
118
     * @deprecated Should use getListAction() method instead
119
     *
120
     * @return \Phalcon\Http\ResponseInterface
121
     */
122
    public function getAllAction() {
123
        return $this->getListAction();
124
    }
125
    
126
    /**
127
     * Retrieving a record list
128
     *
129
     * @return \Phalcon\Http\ResponseInterface
130
     * @throws \Exception
131
     */
132
    public function getListAction()
133
    {
134
        $model = $this->getModelClassName();
135
        
136
        /** @var Resultset $with */
137
        $find = $this->getFind();
138
        $with = $model::with($this->getListWith() ? : [], $find ? : []);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getListWith() targeting Zemit\Mvc\Controller\Rest::getListWith() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
139
        
140
        /**
141
         * Expose the list
142
         * @var int $key
143
         * @var \Zemit\Mvc\Model $item
144
         */
145
        $list = [];
146
        foreach ($with as $key => $item) {
147
            $list[$key] = $item->expose($this->getListExpose());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getListExpose() targeting Zemit\Mvc\Controller\Rest::getListExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
148
        }
149
        
150
        $list = is_array($list) ? array_values(array_filter($list)) : $list;
0 ignored issues
show
introduced by
The condition is_array($list) is always true.
Loading history...
151
        $this->view->list = $list;
152
        $this->view->listCount = count($list);
153
        $this->view->totalCount = $model::find($this->getFindCount($find));
154
        $this->view->totalCount = is_int($this->view->totalCount)? $this->view->totalCount : count($this->view->totalCount); // @todo fix count to work with rollup when joins
155
        $this->view->limit = $find['limit'] ?? false;
156
        $this->view->offset = $find['offset'] ?? false;
157
        $this->view->find = ($this->config->app->debug || $this->config->debug->enable) ? $find : false;
0 ignored issues
show
Bug Best Practice introduced by
The property app does not exist on Zemit\Bootstrap\Config. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property debug does not exist on Zemit\Bootstrap\Config. Since you implemented __get, consider adding a @property annotation.
Loading history...
158
        
159
        return $this->setRestResponse();
160
    }
161
    
162
    /**
163
     * Exporting a record list into a CSV stream
164
     *
165
     * @return \Phalcon\Http\ResponseInterface
166
     * @throws \League\Csv\CannotInsertRecord
167
     * @throws \Zemit\Exception
168
     */
169
    public function exportAction()
170
    {
171
        $model = $this->getModelClassName();
172
        $params = $this->view->getParamsToView();
173
        $contentType = $this->getContentType();
174
        $fileName = ucfirst(Slug::generate(basename(str_replace('\\', '/', $this->getModelClassName())))) . ' List (' . date('Y-m-d') . ')';
175
        
176
        /** @var Resultset $with */
177
        $find = $this->getFind();
178
        $with = $model::with($this->getListWith() ? : [], $find ? : []);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getListWith() targeting Zemit\Mvc\Controller\Rest::getListWith() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
179
180
        /**
181
         * Expose the list
182
         * @var int $key
183
         * @var \Zemit\Mvc\Model $item
184
         */
185
        $list = [];
186
        foreach ($with as $key => $item) {
187
            $list[$key] = $item->expose($this->getExportExpose());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExportExpose() targeting Zemit\Mvc\Controller\Rest::getExportExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
188
        }
189
190
        $list = is_array($list) ? array_values(array_filter($list)) : $list;
0 ignored issues
show
introduced by
The condition is_array($list) is always true.
Loading history...
191
        $this->flatternArrayForCsv($list);
192
		$this->formatColumnText($list);
193
194
        if ($contentType === 'json') {
195
//            $this->response->setJsonContent($list);
196
            $this->response->setContent(json_encode($list, JSON_PRETTY_PRINT, 2048));
197
            $this->response->setContentType('application/json');
198
            $this->response->setHeader('Content-disposition', 'attachment; filename="'.addslashes($fileName).'.json"');
199
            return $this->response->send();
200
        }
201
        
202
        // CSV
203
        if ($contentType === 'csv') {
204
            
205
            // Get CSV custom request parameters
206
            $mode = $params['mode'] ?? null;
207
            $delimiter = $params['delimiter'] ?? null;
208
            $newline = $params['newline'] ?? null;
209
            $escape = $params['escape'] ?? null;
210
            $outputBOM = $params['outputBOM'] ?? null;
211
            $skipIncludeBOM = $params['skipIncludeBOM'] ?? null;
212
            
213
//            $csv = Writer::createFromFileObject(new \SplTempFileObject());
214
            $csv = Writer::createFromStream(fopen('php://memory', 'r+'));
215
            
216
            // CSV - MS Excel on MacOS
217
            if ($mode === 'mac') {
218
                $csv->setOutputBOM(Writer::BOM_UTF16_LE); // utf-16
219
                $csv->setDelimiter("\t"); // tabs separated
220
                $csv->setNewline("\r\n"); // new lines
221
                CharsetConverter::addTo($csv, 'UTF-8', 'UTF-16');
222
            }
223
            
224
            // CSV - MS Excel on Windows
225
            else {
226
                $csv->setOutputBOM(Writer::BOM_UTF8); // utf-8
227
                $csv->setDelimiter(','); // comma separated
228
                $csv->setNewline("\n"); // new line windows
229
                CharsetConverter::addTo($csv, 'UTF-8', 'UTF-8');
230
            }
231
            
232
            // Apply forced params from request
233
            if (isset($outputBOM)) {
234
                $csv->setOutputBOM($outputBOM);
235
            }
236
            if (isset($delimiter)) {
237
                $csv->setDelimiter($delimiter);
238
            }
239
            if (isset($newline)) {
240
                $csv->setNewline($newline);
241
            }
242
            if (isset($escape)) {
243
                $csv->setEscape($escape);
244
            }
245
            if ($skipIncludeBOM) {
246
                $csv->skipInputBOM();
247
            }
248
            else {
249
                $csv->includeInputBOM();
250
            }
251
            
252
            // CSV
253
            $csv->insertOne(array_keys($list[0]));
254
            $csv->insertAll($list);
255
            $csv->output($fileName . '.csv');
256
            die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
257
        }
258
        
259
        // XLSX
260
        if ($contentType === 'xlsx') {
261
            $xlsxArray = [];
262
            foreach ($list as $array) {
263
                if (empty($xlsxArray)) {
264
                    $xlsxArray []= array_keys($array);
265
                }
266
                $xlsxArray []= array_values($array);
267
            }
268
            $xlsx = \SimpleXLSXGen::fromArray($xlsxArray);
269
            $xlsx->downloadAs($fileName . '.xlsx');
270
            die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
271
        }
272
        
273
        // Something went wrong
274
        throw new \Exception('Failed to export `' . $this->getModelClassName() . '` using content-type `' . $contentType . '`', 400);
275
    }
276
277
    /**
278
     * @param array|null $array
279
     *
280
     * @return array|null
281
     */
282
    public function flatternArrayForCsv(?array &$list = null) {
283
284
        foreach ($list as $listKey => $listValue) {
285
            foreach ($listValue as $column => $value) {
286
                if (is_array($value) || is_object($value)) {
287
                    $value = $this->concatListFieldElementForCsv($value, ' ');
288
                    $list[$listKey][$column] = $this->arrayFlatten($value , $column);
0 ignored issues
show
Coding Style introduced by
Space found before comma in argument list
Loading history...
Bug introduced by
It seems like $value can also be of type object; however, parameter $array of Zemit\Mvc\Controller\Rest::arrayFlatten() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
                    $list[$listKey][$column] = $this->arrayFlatten(/** @scrutinizer ignore-type */ $value , $column);
Loading history...
289
                    if (is_array($list[$listKey][$column])) {
290
                        foreach ($list[$listKey][$column] as $childKey => $childValue) {
291
                            $list[$listKey][$childKey] = $childValue;
292
                            unset ($list[$listKey][$column]);
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
293
                        }
294
                    }
295
                }
296
            }
297
        }
298
    }
299
300
    /**
301
     * @param array|object $list
302
     * @param string|null $seperator
303
     *
304
     * @return array|object
305
     */
306
    public function concatListFieldElementForCsv($list, $seperator = ' ') {
307
        foreach ($list as $valueKey => $element) {
308
            if (is_array($element) || is_object($element)) {
309
                $lastKey = array_key_last($list);
0 ignored issues
show
Bug introduced by
It seems like $list can also be of type object; however, parameter $array of array_key_last() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

309
                $lastKey = array_key_last(/** @scrutinizer ignore-type */ $list);
Loading history...
310
                if ($valueKey === $lastKey) {
311
                    continue;
312
                }
313
                foreach ($element as $elKey => $elValue) {
314
                    $list[$lastKey][$elKey] .= $seperator . $elValue;
315
                    if ($lastKey != $valueKey) {
316
                        unset($list[$valueKey]);
317
                    }
318
                }
319
            }
320
        }
321
322
        return $list;
323
    }
324
325
    /**
326
     * @param array|null $array
327
     * @param string|null $alias
328
     *
329
     * @return array|null
330
     */
331
    function arrayFlatten(?array $array, ?string $alias = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for arrayFlatten.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
332
        $return = array();
333
        foreach ($array as $key => $value) {
334
            if (is_array($value)) {
335
                $return = array_merge($return, $this->arrayFlatten($value, $alias));
336
            }
337
            else {
338
                $return[$alias . '.' . $key] = $value;
339
            }
340
        }
341
        return $return;
342
    }
343
344
    /**
345
     * @param array|null $listValue
346
     *
347
     * @return array|null
348
     */
349
    public function mergeColumns (?array $listValue) {
0 ignored issues
show
Coding Style introduced by
Expected "function abc(...)"; found "function abc (...)"
Loading history...
Coding Style introduced by
Expected 0 spaces before opening parenthesis; 1 found
Loading history...
350
        $columnToMergeList = $this->getExportMergeColum ();
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
Bug introduced by
Are you sure the assignment to $columnToMergeList is correct as $this->getExportMergeColum() targeting Zemit\Mvc\Controller\Rest::getExportMergeColum() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
351
        if (!$columnToMergeList || empty($columnToMergeList)) {
0 ignored issues
show
introduced by
$columnToMergeList is of type null, thus it always evaluated to false.
Loading history...
352
            return  $listValue;
353
        }
354
355
        $columnList = [];
356
        foreach ($columnToMergeList as $key => $columnToMerge) {
357
            foreach ($columnToMerge['columns'] as $column) {
358
                if (isset($listValue[$column])) {
359
                    $columnList[] = $listValue[$column];
360
                    unset($listValue[$column]);
361
                }
362
            }
363
            $listValue[$columnToMerge['name']] = implode (' ', $columnList ?? []);
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
364
        }
365
366
        return $listValue;
367
    }
368
369
    /**
370
     * @param array|null $list
371
     *
372
     * @return array|null
373
     */
374
    public function formatColumnText (?array &$list) {
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces before opening parenthesis; 1 found
Loading history...
Coding Style introduced by
Expected "function abc(...)"; found "function abc (...)"
Loading history...
375
        foreach ($list as $listKey => $listValue) {
376
377
            $mergeColumArray = $this->mergeColumns($listValue);
378
            if(!empty($mergeColumArray)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
379
                $list[$listKey] = $mergeColumArray;
380
            }
381
382
            $formatArray = $this->getExportFormatFieldText ($listValue);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $formatArray is correct as $this->getExportFormatFieldText($listValue) targeting Zemit\Mvc\Controller\Res...ExportFormatFieldText() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
383
            if ($formatArray) {
384
                $columNameList = array_keys($formatArray);
0 ignored issues
show
Bug introduced by
$formatArray of type void is incompatible with the type array expected by parameter $array of array_keys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

384
                $columNameList = array_keys(/** @scrutinizer ignore-type */ $formatArray);
Loading history...
385
                foreach ($formatArray as $formatKey => $formatValue) {
0 ignored issues
show
Bug introduced by
The expression $formatArray of type void is not traversable.
Loading history...
386
                    if (isset($formatValue['text'])) {
387
                        $list[$listKey][$formatKey] = $formatValue['text'];
388
                    }
389
390
                    if (isset($formatValue['rename'])) {
391
392
                        $list[$listKey][$formatValue['rename']] = $formatValue['text'] ?? ($list[$listKey][$formatKey] ?? null);
393
                        if ($formatValue['rename'] !== $formatKey) {
394
                            foreach ($columNameList as $columnKey => $columnValue) {
395
396
                                if ($formatKey === $columnValue) {
397
                                    $columNameList[$columnKey] = $formatValue['rename'];
398
                                }
399
                            }
400
401
                            unset($list[$listKey][$formatKey]);
402
                        }
403
                    }
404
                }
405
406
                if (isset($formatArray['reorderColumns']) && $formatArray['reorderColumns']) {
407
                    $list[$listKey] = $this->arrayCustomOrder($list[$listKey], $columNameList);
408
                }
409
            }
410
        }
411
412
        return $list;
413
    }
414
415
    /**
416
     * @param array $arrayToOrder
417
     * @param array $orderList
418
     *
419
     * @return array
420
     */
421
    function arrayCustomOrder($arrayToOrder, $orderList) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for arrayCustomOrder.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
422
        $ordered = array();
423
        foreach ($orderList as $key) {
424
            if (array_key_exists($key, $arrayToOrder)) {
425
                $ordered[$key] = $arrayToOrder[$key];
426
            }
427
        }
428
        return $ordered;
429
    }
430
431
    /**
432
     * Count a record list
433
     * @TODO add total count / deleted count / active count
434
     *
435
     * @return \Phalcon\Http\ResponseInterface
436
     */
437
    public function countAction()
438
    {
439
        $model = $this->getModelClassName();
440
        
441
        /** @var \Zemit\Mvc\Model $entity */
442
        $entity = new $model();
443
        
444
        $this->view->totalCount = $model::count($this->getFindCount($this->getFind()));
445
        $this->view->totalCount = is_int($this->view->totalCount)? $this->view->totalCount : count($this->view->totalCount);
446
        $this->view->model = get_class($entity);
447
        $this->view->source = $entity->getSource();
448
        
449
        return $this->setRestResponse();
450
    }
451
    
452
    /**
453
     * Prepare a new model for the frontend
454
     *
455
     * @return \Phalcon\Http\ResponseInterface
456
     */
457
    public function newAction()
458
    {
459
        $model = $this->getModelClassName();
460
        
461
        /** @var \Zemit\Mvc\Model $entity */
462
        $entity = new $model();
463
        $entity->assign($this->getParams(), $this->getWhiteList(), $this->getColumnMap());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getColumnMap() targeting Zemit\Mvc\Controller\Rest::getColumnMap() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->getWhiteList() targeting Zemit\Mvc\Controller\Rest::getWhiteList() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
464
        
465
        $this->view->model = get_class($entity);
466
        $this->view->source = $entity->getSource();
467
        $this->view->single = $entity->expose($this->getExpose());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
468
        
469
        return $this->setRestResponse();
470
    }
471
    
472
    /**
473
     * Prepare a new model for the frontend
474
     *
475
     * @return \Phalcon\Http\ResponseInterface
476
     */
477
    public function validateAction($id = null)
478
    {
479
        $model = $this->getModelClassName();
480
        
481
        /** @var \Zemit\Mvc\Model $entity */
482
        $entity = $this->getSingle($id);
483
        $new = !$entity;
0 ignored issues
show
introduced by
$entity is of type Zemit\Mvc\Model, thus it always evaluated to true.
Loading history...
484
        
485
        if ($new) {
0 ignored issues
show
introduced by
The condition $new is always false.
Loading history...
486
            $entity = new $model();
487
        }
488
        
489
        $entity->assign($this->getParams(), $this->getWhiteList(), $this->getColumnMap());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getColumnMap() targeting Zemit\Mvc\Controller\Rest::getColumnMap() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->getWhiteList() targeting Zemit\Mvc\Controller\Rest::getWhiteList() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
490
        
491
        /**
492
         * Event to run
493
         * @see https://docs.phalcon.io/4.0/en/db-models-events
494
         */
495
        $events = [
496
            'beforeCreate' => null,
497
            'beforeUpdate' => null,
498
            'beforeSave' => null,
499
            'beforeValidationOnCreate' => null,
500
            'beforeValidationOnUpdate' => null,
501
            'beforeValidation' => null,
502
            'prepareSave' => null,
503
            'validation' => null,
504
            'afterValidationOnCreate' => null,
505
            'afterValidationOnUpdate' => null,
506
            'afterValidation' => null,
507
        ];
508
        
509
        // run events, as it would normally
510
        foreach ($events as $event => $state) {
511
            $this->skipped = false;
0 ignored issues
show
Bug Best Practice introduced by
The property skipped does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
512
            
513
            // skip depending wether it's a create or update
514
            if (strpos($event, $new ? 'Update' : 'Create') !== false) {
515
                continue;
516
            }
517
            
518
            // fire the event, allowing to fail or skip
519
            $events[$event] = $entity->fireEventCancel($event);
520
            if ($events[$event] === false) {
521
                // event failed
522
                break;
523
            }
524
            
525
            // event was skipped, just for consistencies purpose
526
            if ($this->skipped) {
527
                continue;
528
            }
529
        }
530
        
531
        $this->view->model = get_class($entity);
532
        $this->view->source = $entity->getSource();
533
        $this->view->single = $entity->expose($this->getExpose());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
534
        $this->view->messages = $entity->getMessages();
535
        $this->view->events = $events;
536
        $this->view->validated = empty($this->view->messages);
537
        
538
        return $this->setRestResponse($this->view->validated);
0 ignored issues
show
Bug introduced by
$this->view->validated of type boolean is incompatible with the type array|null expected by parameter $response of Zemit\Mvc\Controller\Rest::setRestResponse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

538
        return $this->setRestResponse(/** @scrutinizer ignore-type */ $this->view->validated);
Loading history...
539
    }
540
    
541
    /**
542
     * Saving a record
543
     * - Create
544
     * - Update
545
     *
546
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
547
     *
548
     * @return array
549
     */
550
    public function saveAction($id = null)
551
    {
552
        $this->view->setVars($this->save($id));
0 ignored issues
show
Bug introduced by
The method setVars() does not exist on Phalcon\Mvc\ViewInterface. Did you maybe mean setVar()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

552
        $this->view->/** @scrutinizer ignore-call */ 
553
                     setVars($this->save($id));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
553
        
554
        return $this->setRestResponse($this->view->saved);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->setRestResponse($this->view->saved) returns the type Phalcon\Http\ResponseInterface which is incompatible with the documented return type array.
Loading history...
555
    }
556
    
557
    /**
558
     * Deleting a record
559
     *
560
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
561
     *
562
     * @return bool
563
     */
564
    public function deleteAction($id = null)
565
    {
566
        $single = $this->getSingle($id);
567
        
568
        $this->view->deleted = $single ? $single->delete() : false;
569
        $this->view->single = $single ? $single->expose($this->getExpose()) : false;
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
570
        $this->view->messages = $single ? $single->getMessages() : false;
571
        
572
        if (!$single) {
573
            $this->response->setStatusCode(404, 'Not Found');
574
            
575
            return false;
576
        }
577
        
578
        return $this->setRestResponse($this->view->deleted);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->setRestRes...e($this->view->deleted) returns the type Phalcon\Http\ResponseInterface which is incompatible with the documented return type boolean.
Loading history...
Bug introduced by
$this->view->deleted of type boolean is incompatible with the type array|null expected by parameter $response of Zemit\Mvc\Controller\Rest::setRestResponse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

578
        return $this->setRestResponse(/** @scrutinizer ignore-type */ $this->view->deleted);
Loading history...
579
    }
580
    
581
    /**
582
     * Restoring record
583
     *
584
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
585
     *
586
     * @return bool
587
     */
588
    public function restoreAction($id = null)
589
    {
590
        $single = $this->getSingle($id);
591
        
592
        $this->view->restored = $single ? $single->restore() : false;
0 ignored issues
show
Bug introduced by
The method restore() does not exist on Phalcon\Mvc\Model\Resultset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

592
        $this->view->restored = $single ? $single->/** @scrutinizer ignore-call */ restore() : false;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
593
        $this->view->single = $single ? $single->expose($this->getExpose()) : false;
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
594
        $this->view->messages = $single ? $single->getMessages() : false;
595
        
596
        if (!$single) {
597
            $this->response->setStatusCode(404, 'Not Found');
598
            
599
            return false;
600
        }
601
        
602
        return $this->setRestResponse($this->view->restored);
0 ignored issues
show
Bug introduced by
It seems like $this->view->restored can also be of type false; however, parameter $response of Zemit\Mvc\Controller\Rest::setRestResponse() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

602
        return $this->setRestResponse(/** @scrutinizer ignore-type */ $this->view->restored);
Loading history...
Bug Best Practice introduced by
The expression return $this->setRestRes...($this->view->restored) returns the type Phalcon\Http\ResponseInterface which is incompatible with the documented return type boolean.
Loading history...
603
    }
604
    
605
    /**
606
     * Re-ordering a position
607
     *
608
     * @param null $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
609
     * @param null $position
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $position is correct as it would always require null to be passed?
Loading history...
610
     *
611
     * @return bool|\Phalcon\Http\ResponseInterface
612
     */
613
    public function reorderAction($id = null)
614
    {
615
        $single = $this->getSingle($id);
616
        
617
        $position = $this->getParam('position', 'int');
618
        
619
        $this->view->reordered = $single ? $single->reorder($position) : false;
0 ignored issues
show
Bug introduced by
The method reorder() does not exist on Phalcon\Mvc\Model\Resultset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

619
        $this->view->reordered = $single ? $single->/** @scrutinizer ignore-call */ reorder($position) : false;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
620
        $this->view->single = $single ? $single->expose($this->getExpose()) : false;
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Rest::getExpose() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
621
        $this->view->messages = $single ? $single->getMessages() : false;
622
        
623
        if (!$single) {
624
            $this->response->setStatusCode(404, 'Not Found');
625
            
626
            return false;
627
        }
628
        
629
        return $this->setRestResponse($this->view->reordered);
0 ignored issues
show
Bug introduced by
It seems like $this->view->reordered can also be of type false; however, parameter $response of Zemit\Mvc\Controller\Rest::setRestResponse() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

629
        return $this->setRestResponse(/** @scrutinizer ignore-type */ $this->view->reordered);
Loading history...
630
    }
631
    
632
    /**
633
     * Sending an error as an http response
634
     *
635
     * @param null $error
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $error is correct as it would always require null to be passed?
Loading history...
636
     * @param null $response
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $response is correct as it would always require null to be passed?
Loading history...
637
     *
638
     * @return \Phalcon\Http\ResponseInterface
639
     */
640
    public function setRestErrorResponse($code = 400, $status = 'Bad Request', $response = null)
641
    {
642
        return $this->setRestResponse($response, $code, $status);
643
    }
644
    
645
    /**
646
     * Sending rest response as an http response
647
     *
648
     * @param array|null $response
649
     * @param null $status
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $status is correct as it would always require null to be passed?
Loading history...
650
     * @param null $code
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $code is correct as it would always require null to be passed?
Loading history...
651
     * @param int $jsonOptions
652
     * @param int $depth
653
     *
654
     * @return \Phalcon\Http\ResponseInterface
655
     */
656
    public function setRestResponse($response = null, $code = null, $status = null, $jsonOptions = 0, $depth = 512)
657
    {
658
        $debug = $this->config->app->debug ?? false;
0 ignored issues
show
Bug Best Practice introduced by
The property app does not exist on Zemit\Bootstrap\Config. Since you implemented __get, consider adding a @property annotation.
Loading history...
659
        
660
        // keep forced status code or set our own
661
        $responseStatusCode = $this->response->getStatusCode();
662
        $reasonPhrase = $this->response->getReasonPhrase();
663
        $status ??= $reasonPhrase ? : 'OK';
664
        $code ??= (int)$responseStatusCode ? : 200;
665
        $view = $this->view->getParamsToView();
666
        $hash = hash('sha512', json_encode($view));
667
        
668
        /**
669
         * Debug section
670
         * - Versions
671
         * - Request
672
         * - Identity
673
         * - Profiler
674
         * - Dispatcher
675
         * - Router
676
         */
677
        $request = $debug ? $this->request->toArray() : null;
0 ignored issues
show
introduced by
The method toArray() does not exist on Phalcon\Http\Request. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

677
        $request = $debug ? $this->request->/** @scrutinizer ignore-call */ toArray() : null;
Loading history...
Bug introduced by
The method toArray() does not exist on Phalcon\Http\RequestInterface. It seems like you code against a sub-type of Phalcon\Http\RequestInterface such as Zemit\Http\Request. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

677
        $request = $debug ? $this->request->/** @scrutinizer ignore-call */ toArray() : null;
Loading history...
678
        $identity = $debug ? $this->identity->getIdentity() : null;
679
        $profiler = $debug && $this->profiler ? $this->profiler->toArray() : null;
680
        $dispatcher = $debug ? $this->dispatcher->toArray() : null;
681
        $router = $debug ? $this->router->toArray() : null;
682
        
683
        $api = $debug ? [
684
            'php' => phpversion(),
685
            'phalcon' => Version::get(),
686
            'zemit' => $this->config->core->version,
0 ignored issues
show
Bug Best Practice introduced by
The property core does not exist on Zemit\Bootstrap\Config. Since you implemented __get, consider adding a @property annotation.
Loading history...
687
            'core' => $this->config->core->name,
688
            'app' => $this->config->app->version,
689
            'name' => $this->config->app->name,
690
        ] : [];
691
        $api['version'] = '0.1';
692
        
693
        $this->response->setStatusCode($code, $code . ' ' . $status);
694
        
695
        // @todo handle this correctly
696
        // @todo private vs public cache type
697
        $cache = $this->getCache();
698
        if (!empty($cache['lifetime'])) {
699
            if ($this->response->getStatusCode() === 200) {
700
                $this->response->setCache($cache['lifetime']);
701
                $this->response->setEtag($hash);
702
            }
703
        } else {
704
            $this->response->setCache(0);
705
            $this->response->setHeader('Cache-Control', 'no-cache, max-age=0');
706
        }
707
        
708
        return $this->response->setJsonContent(array_merge([
709
            'api' => $api,
710
            'timestamp' => date('c'),
711
            'hash' => $hash,
712
            'status' => $status,
713
            'code' => $code,
714
            'response' => $response,
715
            'view' => $view,
716
        ], $debug ? [
717
            'identity' => $identity,
718
            'profiler' => $profiler,
719
            'request' => $request,
720
            'dispatcher' => $dispatcher,
721
            'router' => $router,
722
        ] : []), $jsonOptions, $depth);
0 ignored issues
show
Unused Code introduced by
The call to Phalcon\Http\ResponseInterface::setJsonContent() has too many arguments starting with $jsonOptions. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

722
        return $this->response->/** @scrutinizer ignore-call */ setJsonContent(array_merge([

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
723
    }
724
    
725
    /**
726
     * Handle rest response automagically
727
     *
728
     * @param Dispatcher $dispatcher
729
     */
730
    public function afterExecuteRoute(Dispatcher $dispatcher)
731
    {
732
        $response = $dispatcher->getReturnedValue();
733
        
734
        // Avoid breaking default phalcon behaviour
735
        if ($response instanceof Response) {
736
            return;
737
        }
738
        
739
        // Merge response into view variables
740
        if (is_array($response)) {
741
            $this->view->setVars($response, true);
742
        }
743
        
744
        // Return our Rest normalized response
745
        $dispatcher->setReturnedValue($this->setRestResponse(is_array($response) ? null : $response));
746
    }
747
}
748