Test Failed
Push — master ( 4abc3b...a9eb80 )
by Julien
04:29
created

Model::saveEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 9
ccs 0
cts 8
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
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 Phalcon\Db\Column;
14
use Phalcon\Messages\Message;
15
use Phalcon\Messages\Messages;
16
use Phalcon\Mvc\Model\Resultset;
17
use Phalcon\Mvc\ModelInterface;
18
use Phalcon\Text;
19
use Zemit\Http\Request;
20
use Zemit\Identity;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Zemit\Mvc\Controller\Identity. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use Zemit\Mvc\Model\Expose\Expose;
22
use Zemit\Utils\Slug;
23
24
/**
25
 * Trait Model
26
 *
27
 * @author Julien Turbide <[email protected]>
28
 * @copyright Zemit Team <[email protected]>
29
 *
30
 * @since 1.0
31
 * @version 1.0
32
 *
33
 * @package Zemit\Mvc\Controller
34
 */
35
trait Model
36
{
37
    protected $_bind = [];
38
    protected $_bindTypes = [];
39
    
40
    /**
41
     * Get the current Model Name
42
     * @return string|null
43
     * @todo remove for v1
44
     *
45
     * @deprecated change to getModelClassName() instead
46
     */
47
    public function getModelName()
48
    {
49
        return $this->getModelClassName();
50
    }
51
    
52
    /**
53
     * Get the current Model Class Name
54
     *
55
     * @return string|null
56
     */
57
    public function getModelClassName()
58
    {
59
        return $this->getModelNameFromController();
60
    }
61
    
62
    /**
63
     * Get the WhiteList parameters for saving
64
     * @return null|array
65
     * @todo add a whitelist object that would be able to support one configuration for the search, assign, filter
66
     *
67
     */
68
    protected function getWhiteList()
69
    {
70
        return null;
71
    }
72
    
73
    /**
74
     * Get the Flattened WhiteList
75
     *
76
     * @param array|null $whiteList
77
     *
78
     * @return array|null
79
     */
80
    public function getFlatWhiteList(?array $whiteList = null)
81
    {
82
        $whiteList ??= $this->getWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWhiteList() targeting Zemit\Mvc\Controller\Model::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...
83
        
84
        return array_keys(Expose::_parseColumnsRecursive($whiteList));
0 ignored issues
show
Bug introduced by
It seems like Zemit\Mvc\Model\Expose\E...nsRecursive($whiteList) can also be of type null; however, parameter $array of array_keys() 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

84
        return array_keys(/** @scrutinizer ignore-type */ Expose::_parseColumnsRecursive($whiteList));
Loading history...
85
    }
86
    
87
    /**
88
     * Get the WhiteList parameters for filtering
89
     *
90
     * @return null|array
91
     */
92
    protected function getFilterWhiteList()
93
    {
94
        return $this->getWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWhiteList() targeting Zemit\Mvc\Controller\Model::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...
95
    }
96
    
97
    /**
98
     * Get the WhiteList parameters for filtering
99
     *
100
     * @return null|array
101
     */
102
    protected function getSearchWhiteList()
103
    {
104
        return $this->getFilterWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getFilterWhiteList() targeting Zemit\Mvc\Controller\Model::getFilterWhiteList() 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...
105
    }
106
    
107
    /**
108
     * Get the column mapping for crud
109
     *
110
     * @return null|array
111
     */
112
    protected function getColumnMap()
113
    {
114
        return null;
115
    }
116
    
117
    /**
118
     * Get relationship eager loading definition
119
     *
120
     * @return null|array
121
     */
122
    protected function getWith()
123
    {
124
        return null;
125
    }
126
    
127
    /**
128
     * Get relationship eager loading definition for a listing
129
     *
130
     * @return null|array
131
     */
132
    protected function getListWith()
133
    {
134
        return $this->getWith();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWith() targeting Zemit\Mvc\Controller\Model::getWith() 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...
135
    }
136
    
137
    /**
138
     * Get expose definition for a single entity
139
     *
140
     * @return null|array
141
     */
142
    protected function getExpose()
143
    {
144
        return null;
145
    }
146
    
147
    /**
148
     * Get expose definition for listing many entities
149
     *
150
     * @return null|array
151
     */
152
    protected function getListExpose()
153
    {
154
        return $this->getExpose();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Model::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...
155
    }
156
    
157
    /**
158
     * Get expose definition for export
159
     *
160
     * @return null|array
161
     */
162
    protected function getExportExpose()
163
    {
164
        return $this->getExpose();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Model::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...
165
    }
166
167
    /**
168
     * Get columns merge definition for export
169
     *
170
     * @return null|array
171
     */
172
    public function getExportMergeColum ()
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...
173
    {
174
        return null;
175
    }
176
177
    /**
178
     * Get columns format field text definition for export
179
     *
180
     * @param array|null $params
181
     *
182
     * @return null|array
183
     */
184
    public function getExportFormatFieldText (?array $params = null) {
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...
185
        return null;
186
    }
187
    
188
    /**
189
     * Get join definition
190
     *
191
     * @return null|array
192
     */
193
    protected function getJoins()
194
    {
195
        return null;
196
    }
197
    
198
    /**
199
     * Get the order definition
200
     *
201
     * @return null|array
202
     */
203
    protected function getOrder()
204
    {
205
        return $this->getParamExplodeArrayMapFilter('order');
206
    }
207
    
208
    /**
209
     * Get the current limit value
210
     *
211
     * @return null|int Default: 1000
212
     */
213
    protected function getLimit(): int
214
    {
215
        return (int)$this->getParam('limit', 'int', 1000);
216
    }
217
    
218
    /**
219
     * Get the current offset value
220
     *
221
     * @return null|int Default: 0
222
     */
223
    protected function getOffset(): int
224
    {
225
        return (int)$this->getParam('offset', 'int', 0);
226
    }
227
    
228
    /**
229
     * Get group
230
     * - Automatically group by ID by default if nothing else is provided
231
     * - This will fix multiple single records being returned for the same model with joins
232
     *
233
     * @return array[string]|string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array[string]|string|null at position 1 could not be parsed: Expected ']' at position 1, but found '['.
Loading history...
234
     */
235
    protected function getGroup()
236
    {
237
        $group = $this->getParamExplodeArrayMapFilter('group');
238
        
239
        // Fix for joins, automatically append grouping if none provided
240
        $join = $this->getJoins();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $join is correct as $this->getJoins() targeting Zemit\Mvc\Controller\Model::getJoins() 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...
241
        if (empty($group) && !empty($join)) {
242
            $group = $this->appendModelName('id');
243
        }
244
        
245
        return $group;
246
    }
247
    
248
    /**
249
     * Get distinct
250
     * @TODO see how to implement this, maybe an action itself
251
     *
252
     * @return array[string]|string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array[string]|string|null at position 1 could not be parsed: Expected ']' at position 1, but found '['.
Loading history...
253
     */
254
    protected function getDistinct()
255
    {
256
        return $this->getParamExplodeArrayMapFilter('distinct');
257
    }
258
    
259
    /**
260
     * Get columns
261
     * @TODO see how to implement this
262
     *
263
     * @return array[string]|string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array[string]|string|null at position 1 could not be parsed: Expected ']' at position 1, but found '['.
Loading history...
264
     */
265
    protected function getColumns()
266
    {
267
        return $this->getParam('columns', 'string');
268
    }
269
    
270
    /**
271
     * Return the whitelisted role list for the current model
272
     *
273
     * @return string[] By default will return dev and admin role
274
     */
275
    protected function getRoleList() {
276
        return ['dev', 'admin'];
277
    }
278
    
279
    /**
280
     * Get Search condition
281
     *
282
     * @return string Default: deleted = 0
283
     */
284
    protected function getSearchCondition()
285
    {
286
        $conditions = [];
287
        
288
        $searchList = array_values(array_filter(array_unique(explode(' ', $this->getParam('search', 'string')))));
0 ignored issues
show
Bug introduced by
It seems like $this->getParam('search', 'string') can also be of type null and string[]; however, parameter $string of explode() does only seem to accept string, 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
        $searchList = array_values(array_filter(array_unique(explode(' ', /** @scrutinizer ignore-type */ $this->getParam('search', 'string')))));
Loading history...
289
        
290
        foreach ($searchList as $searchTerm) {
291
            $orConditions = [];
292
            $searchWhiteList = $this->getSearchWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $searchWhiteList is correct as $this->getSearchWhiteList() targeting Zemit\Mvc\Controller\Model::getSearchWhiteList() 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...
293
            if ($searchWhiteList) {
294
                foreach ($searchWhiteList as $whiteList) {
0 ignored issues
show
Bug introduced by
The expression $searchWhiteList of type void is not traversable.
Loading history...
295
                    
296
                    // Multidimensional arrays not supported yet
297
                    // @todo support this properly
298
                    if (is_array($whiteList)) {
299
                        continue;
300
                    }
301
                    
302
                    $searchTermBinding = '_' . uniqid() . '_';
303
                    $orConditions [] = $this->appendModelName($whiteList) . " like :$searchTermBinding:";
0 ignored issues
show
Bug introduced by
Are you sure $this->appendModelName($whiteList) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

303
                    $orConditions [] = /** @scrutinizer ignore-type */ $this->appendModelName($whiteList) . " like :$searchTermBinding:";
Loading history...
304
                    $this->setBind([$searchTermBinding => '%' . $searchTerm . '%']);
305
                    $this->setBindTypes([$searchTermBinding => Column::BIND_PARAM_STR]);
306
                }
307
            }
308
            
309
            if (!empty($orConditions)) {
310
                $conditions [] = '(' . implode(' or ', $orConditions) . ')';
311
            }
312
        }
313
        
314
        return empty($conditions) ? null : '(' . implode(' and ', $conditions) . ')';
315
    }
316
    
317
    /**
318
     * Get Soft delete condition
319
     *
320
     * @return string Default: deleted = 0
321
     */
322
    protected function getSoftDeleteCondition(): ?string
323
    {
324
        return '[' . $this->getModelClassName() . '].[deleted] = 0';
325
    }
326
    
327
    /**
328
     * @param $field
329
     * @param string $sanitizer
330
     * @param string $glue
331
     *
332
     * @return array|string[]
333
     */
334
    public function getParamExplodeArrayMapFilter($field, $sanitizer = 'string', $glue = ',')
335
    {
336
        $filter = $this->filter;
337
        $ret = array_filter(array_map(function($e) use ($filter, $sanitizer) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
338
            return $this->appendModelName(trim($filter->sanitize($e, $sanitizer)));
339
        }, explode($glue, $this->getParam($field, $sanitizer))));
0 ignored issues
show
Bug introduced by
It seems like $this->getParam($field, $sanitizer) can also be of type null and string[]; however, parameter $string of explode() does only seem to accept string, 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

339
        }, explode($glue, /** @scrutinizer ignore-type */ $this->getParam($field, $sanitizer))));
Loading history...
340
        
341
        return empty($ret) ? null : $ret;
342
    }
343
    
344
    /**
345
     * Set the variables to bind
346
     *
347
     * @param array $bind Variable bind to merge or replace
348
     * @param bool $replace Pass true to replace the entire bind set
349
     */
350
    public function setBind(array $bind = [], bool $replace = false)
351
    {
352
        $this->_bind = $replace ? $bind : array_merge($this->getBind(), $bind);
0 ignored issues
show
Bug introduced by
It seems like $this->getBind() can also be of type null; however, parameter $arrays of array_merge() 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

352
        $this->_bind = $replace ? $bind : array_merge(/** @scrutinizer ignore-type */ $this->getBind(), $bind);
Loading history...
353
    }
354
    
355
    /**
356
     * Get the current bind
357
     * key => value
358
     *
359
     * @return array|null
360
     */
361
    public function getBind()
362
    {
363
        return $this->_bind ?? null;
364
    }
365
    
366
    /**
367
     * Set the variables types to bind
368
     *
369
     * @param array $bindTypes Variable bind types to merge or replace
370
     * @param bool $replace Pass true to replace the entire bind type set
371
     */
372
    public function setBindTypes(array $bindTypes = [], bool $replace = false)
373
    {
374
        $this->_bindTypes = $replace ? $bindTypes : array_merge($this->getBindTypes(), $bindTypes);
0 ignored issues
show
Bug introduced by
It seems like $this->getBindTypes() can also be of type null; however, parameter $arrays of array_merge() 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

374
        $this->_bindTypes = $replace ? $bindTypes : array_merge(/** @scrutinizer ignore-type */ $this->getBindTypes(), $bindTypes);
Loading history...
375
    }
376
    
377
    /**
378
     * Get the current bind types
379
     *
380
     * @return array|null
381
     */
382
    public function getBindTypes()
383
    {
384
        return $this->_bindTypes ?? null;
385
    }
386
    
387
    /**
388
     * Get Created By Condition
389
     *
390
     * @param string[] $columns
391
     * @param Identity|null $identity
392
     * @param string[]|null $roleList
393
     *
394
     * @return null
395
     *
396
     * @return string|null
397
     */
398
    protected function getIdentityCondition(array $columns = null, Identity $identity = null, $roleList = null)
399
    {
400
        // @todo
401
        if ($this->request->isOptions()){
402
            return null;
403
        }
404
        
405
        $identity ??= $this->identity ?? false;
406
        $roleList ??= $this->getRoleList();
407
        $modelName = $this->getModelClassName();
408
        
409
        if ($modelName && $identity && !$identity->hasRole($roleList)) {
410
            $ret = [];
411
            
412
            $columns ??= [
413
                'createdBy',
414
                'ownedBy',
415
                'userId',
416
            ];
417
            
418
            foreach ($columns as $column) {
419
                if (!property_exists($modelName, $column)) {
420
                    continue;
421
                }
422
                
423
                $field = strpos($column, '.') !== false ? $column : $modelName . '.' . $column;
424
                $field = '[' . str_replace('.', '].[', $field) . ']';
425
                
426
                $this->setBind([$column => (int)$identity->getUserId()]);
427
                $this->setBindTypes([$column => Column::BIND_PARAM_INT]);
428
                $ret [] = $field . ' = :' . $column . ':';
429
            }
430
            
431
            return implode(' or ', $ret);
0 ignored issues
show
Bug Best Practice introduced by
The expression return implode(' or ', $ret) returns the type string which is incompatible with the documented return type null.
Loading history...
432
        }
433
        
434
        return null;
435
    }
436
    
437
    function arrayMapRecursive($callback, $array)
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 arrayMapRecursive.

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...
438
    {
439
        $func = function($item) use (&$func, &$callback) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
440
            return is_array($item) ? array_map($func, $item) : call_user_func($callback, $item);
441
        };
442
        
443
        return array_map($func, $array);
444
    }
445
    
446
    /**
447
     * Get Filter Condition
448
     *
449
     * @param array|null $filters
450
     * @param array|null $whiteList
451
     * @param bool $or
452
     *
453
     * @return string|null Return the generated query
454
     * @throws \Exception Throw an exception if the field property is not valid
455
     * @todo escape fields properly
456
     *
457
     */
458
    protected function getFilterCondition(array $filters = null, array $whiteList = null, $or = false)
459
    {
460
        $filters ??= $this->getParam('filters');
461
        $whiteList ??= $this->getFilterWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getFilterWhiteList() targeting Zemit\Mvc\Controller\Model::getFilterWhiteList() 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...
462
        $whiteList = $this->getFlatWhiteList($whiteList);
463
        $lowercaseWhiteList = !is_null($whiteList) ? $this->arrayMapRecursive('mb_strtolower', $whiteList) : $whiteList;
0 ignored issues
show
introduced by
The condition is_null($whiteList) is always false.
Loading history...
464
        
465
        // No filter, no query
466
        if (empty($filters)) {
467
            return null;
468
        }
469
        
470
        $query = [];
471
        foreach ($filters as $filter) {
472
            $field = $this->filter->sanitize($filter['field'] ?? null, ['string', 'trim']);
473
            
474
            if (!empty($field)) {
475
                $lowercaseField = mb_strtolower($field);
476
                
477
                // whiteList on filter condition
478
                if (is_null($whiteList) || !in_array($lowercaseField, $lowercaseWhiteList, true)) {
479
                    // @todo if config is set to throw exception on usage of not allowed filters otherwise continue looping through
480
                    throw new \Exception('Not allowed to filter using the following field: `' . $field . '`', 403);
481
                    continue;
0 ignored issues
show
Unused Code introduced by
ContinueNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
482
                }
483
                
484
                $uniqid = substr(md5(json_encode($filter)), 0, 10);
485
//                $queryField = '_' . uniqid($uniqid . '_field_') . '_';
486
                $queryValue = '_' . uniqid($uniqid . '_value_') . '_';
487
                $queryOperator = strtolower($filter['operator']);
488
                switch($queryOperator) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after SWITCH keyword; 0 found
Loading history...
489
                    case '=': // Equal operator
490
                    case '!=': // Not equal operator
491
                    case '<>': // Not equal operator
492
                    case '>': // Greater than operator
493
                    case '>=': // Greater than or equal operator
494
                    case '<': // Less than or equal operator
495
                    case '<=': // Less than or equal operator
496
                    case '<=>': // NULL-safe equal to operator
497
                    case 'in': // Whether a value is within a set of values
498
                    case 'not in': // Whether a value is not within a set of values
499
                    case 'like': // Simple pattern matching
500
                    case 'not like': // Negation of simple pattern matching
501
                    case 'between': // Whether a value is within a range of values
502
                    case 'not between': // Whether a value is not within a range of values
503
                    case 'is': // Test a value against a boolean
504
                    case 'is not': // Test a value against a boolean
505
                    case 'is null': // NULL value test
506
                    case 'is not null': // NOT NULL value test
507
                    case 'is false': // Test a value against a boolean
508
                    case 'is not false': // // Test a value against a boolean
509
                    case 'is true': // // Test a value against a boolean
510
                    case 'is not true': // // Test a value against a boolean
511
                        break;
512
                    default:
513
                        throw new \Exception('Not allowed to filter using the following operator: `' . $queryOperator . '`', 403);
514
                        break;
515
                }
516
                
517
                $bind = [];
518
                $bindType = [];
519
520
//                $bind[$queryField] = $filter['field'];
521
//                $bindType[$queryField] = Column::BIND_PARAM_STR;
522
//                $queryFieldBinder = ':' . $queryField . ':';
523
//                $queryFieldBinder = '{' . $queryField . '}';
524
                
525
                // Add the current model name by default
526
                $field = $this->appendModelName($field);
527
                
528
                $queryFieldBinder = $field;
529
                $queryValueBinder = ':' . $queryValue . ':';
530
                if (isset($filter['value'])) {
531
                    // special for between and not between
532
                    if (in_array($queryOperator, ['between', 'not between'])) {
533
                        $queryValue0 = '_' . uniqid($uniqid . '_value_') . '_';
534
                        $queryValue1 = '_' . uniqid($uniqid . '_value_') . '_';
535
                        $bind[$queryValue0] = $filter['value'][0];
536
                        $bind[$queryValue1] = $filter['value'][1];
537
                        $bindType[$queryValue0] = Column::BIND_PARAM_STR;
538
                        $bindType[$queryValue1] = Column::BIND_PARAM_STR;
539
                        $query [] = (($queryOperator === 'not between')? 'not ' : null) . "$queryFieldBinder between :$queryValue0: and :$queryValue1:";
540
                    }
541
                    else {
542
                        $bind[$queryValue] = $filter['value'];
543
                        if (is_string($filter['value'])) {
544
                            $bindType[$queryValue] = Column::BIND_PARAM_STR;
545
                        }
546
                        else if (is_int($filter['value'])) {
547
                            $bindType[$queryValue] = Column::BIND_PARAM_INT;
548
                        }
549
                        else if (is_bool($filter['value'])) {
550
                            $bindType[$queryValue] = Column::BIND_PARAM_BOOL;
551
                        }
552
                        else if (is_float($filter['value'])) {
553
                            $bindType[$queryValue] = Column::BIND_PARAM_DECIMAL;
554
                        }
555
                        else if (is_double($filter['value'])) {
556
                            $bindType[$queryValue] = Column::BIND_PARAM_DECIMAL;
557
                        }
558
                        else if (is_array($filter['value'])) {
559
                            $queryValueBinder = '({' . $queryValue . ':array})';
560
                            $bindType[$queryValue] = Column::BIND_PARAM_STR;
561
                        }
562
                        else {
563
                            $bindType[$queryValue] = Column::BIND_PARAM_NULL;
564
                        }
565
                        $query [] = "$queryFieldBinder $queryOperator $queryValueBinder";
566
                    }
567
                }
568
                else {
569
                    $query [] = "$queryFieldBinder $queryOperator";
570
                }
571
                
572
                $this->setBind($bind);
573
                $this->setBindTypes($bindType);
574
            }
575
            else {
576
                if (is_array($filter) || $filter instanceof \Traversable) {
577
                    $query [] = $this->getFilterCondition($filter, $whiteList, !$or);
0 ignored issues
show
Bug introduced by
It seems like $filter can also be of type Traversable; however, parameter $filters of Zemit\Mvc\Controller\Model::getFilterCondition() 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

577
                    $query [] = $this->getFilterCondition(/** @scrutinizer ignore-type */ $filter, $whiteList, !$or);
Loading history...
578
                }
579
                else {
580
                    throw new \Exception('A valid field property is required.', 400);
581
                }
582
            }
583
        }
584
        
585
        return empty($query) ? null : '(' . implode($or ? ' or ' : ' and ', $query) . ')';
586
    }
587
    
588
    /**
589
     * Append the current model name alias to the field
590
     * So: field -> [Alias].[field]
591
     *
592
     * @param string|array $field
593
     * @param null $modelName
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $modelName is correct as it would always require null to be passed?
Loading history...
594
     *
595
     * @return array|string
596
     */
597
    public function appendModelName($field, $modelName = null)
598
    {
599
        $modelName ??= $this->getModelClassName();
600
        
601
        if (empty($field)) {
602
            return $field;
603
        }
604
        
605
        if (is_string($field)) {
606
            // Add the current model name by default
607
            $explode = explode(' ', $field);
608
            if (!strpos($field, '.') !== false) {
609
                $field = trim('[' . $modelName . '].[' . array_shift($explode) . '] ' . implode(' ', $explode));
610
            }
611
            else if (strpos($field, ']') === false && strpos($field, '[') === false) {
612
                $field = trim('[' . implode('].[', explode('.', array_shift($explode))) . ']' . implode(' ', $explode));
613
            }
614
        }
615
        else if (is_array($field)) {
0 ignored issues
show
introduced by
The condition is_array($field) is always true.
Loading history...
616
            foreach ($field as $fieldKey => $fieldValue) {
617
                $field[$fieldKey] = $this->appendModelName($fieldValue, $modelName);
0 ignored issues
show
Bug introduced by
It seems like $modelName can also be of type string; however, parameter $modelName of Zemit\Mvc\Controller\Model::appendModelName() does only seem to accept 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

617
                $field[$fieldKey] = $this->appendModelName($fieldValue, /** @scrutinizer ignore-type */ $modelName);
Loading history...
618
            }
619
        }
620
        
621
        return $field;
622
    }
623
    
624
    /**
625
     * Get Permission Condition
626
     *
627
     * @return null
628
     */
629
    protected function getPermissionCondition($type = null, $identity = null)
630
    {
631
        return null;
632
    }
633
    
634
    protected function fireGet($method) {
635
        $ret = $this->{$method}();
636
        $eventRet = $this->eventsManager->fire('rest:' . $method, $this, $ret);
637
        return $eventRet === false? null : $eventRet ?? $ret;
638
    }
639
    
640
    /**
641
     * Get all conditions
642
     *
643
     * @return string
644
     * @throws \Exception
645
     */
646
    protected function getConditions()
647
    {
648
        $conditions = array_values(array_unique(array_filter([
649
            $this->fireGet('getSoftDeleteCondition'),
650
            $this->fireGet('getIdentityCondition'),
651
            $this->fireGet('getFilterCondition'),
652
            $this->fireGet('getSearchCondition'),
653
            $this->fireGet('getPermissionCondition'),
654
        ])));
655
        
656
        if (empty($conditions)) {
657
            $conditions []= 1;
658
        }
659
        
660
        return '(' . implode(') and (', $conditions) . ')';
661
    }
662
    
663
    /**
664
     * Get having conditions
665
     */
666
    public function getHaving()
667
    {
668
        return null;
669
    }
670
    
671
    /**
672
     * Get a cache key from params
673
     *
674
     * @param array|null $params
675
     *
676
     * @return string|null
677
     */
678
    public function getCacheKey(?array $params = null): ?string
679
    {
680
        $params ??= $this->getParams();
681
        
682
        return Slug::generate(json_encode($params, JSON_UNESCAPED_SLASHES));
683
    }
684
    
685
    /**
686
     * Get cache setting
687
     *
688
     * @param array|null $params
689
     *
690
     * @return array|null
691
     */
692
    public function getCache(?array $params = null)
693
    {
694
        $params ??= $this->getParams();
695
        
696
        if (!empty($params['cache'])) {
697
            return [
698
                'lifetime' => (int)$params['cache'],
699
                'key' => $this->getCacheKey($params),
700
            ];
701
        }
702
        
703
        return null;
704
    }
705
    
706
    
707
    /**
708
     * Get requested content type
709
     * - Default will return csv
710
     *
711
     * @param array|null $params
712
     *
713
     * @return string
714
     * @throws \Exception
715
     */
716
    public function getContentType(?array $params = null)
717
    {
718
        $params ??= $this->getParams();
719
        
720
        $contentType = strtolower($params['contentType'] ?? $params['content-type'] ?? 'json');
721
        
722
        switch($contentType) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after SWITCH keyword; 0 found
Loading history...
723
            case 'html':
724
            case 'text/html':
725
            case 'application/html':
726
                // html not supported yet
727
                break;
728
            case 'xml':
729
            case 'text/xml':
730
            case 'application/xml':
731
                // xml not supported yet
732
                break;
733
            case 'text':
734
            case 'text/plain':
735
                // plain text not supported yet
736
                break;
737
            case 'json':
738
            case 'text/json':
739
            case 'application/json':
740
                return 'json';
741
            case 'csv':
742
            case 'text/csv':
743
                return 'csv';
744
            case 'xlsx':
745
            case 'application/xlsx':
746
            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
747
                return 'xlsx';
748
            case 'xls':
749
            case 'application/vnd.ms-excel':
750
                // old xls not supported yet
751
                break;
752
        }
753
        
754
        throw new \Exception('`' . $contentType . '` is not supported.', 400);
755
    }
756
    
757
    /**
758
     * Get find definition
759
     *
760
     * @return array
761
     * @throws \Exception
762
     */
763
    protected function getFind()
764
    {
765
        $find = [];
766
        $find['conditions'] = $this->fireGet('getConditions');
767
        $find['bind'] = $this->fireGet('getBind');
768
        $find['bindTypes'] = $this->fireGet('getBindTypes');
769
        $find['limit'] = $this->fireGet('getLimit');
770
        $find['offset'] = $this->fireGet('getOffset');
771
        $find['order'] = $this->fireGet('getOrder');
772
        $find['columns'] = $this->fireGet('getColumns');
773
        $find['distinct'] = $this->fireGet('getDistinct');
774
        $find['joins'] = $this->fireGet('getJoins');
775
        $find['group'] = $this->fireGet('getGroup');
776
        $find['having'] = $this->fireGet('getHaving');
777
        $find['cache'] = $this->fireGet('getCache');
778
        
779
        // fix for grouping by multiple fields, phalcon only allow string here
780
        foreach (['distinct', 'group'] as $findKey) {
781
            if (isset($find[$findKey]) && is_array($find[$findKey])) {
782
                $find[$findKey] = implode(', ', $find[$findKey]);
783
            }
784
        }
785
        
786
        return array_filter($find);
787
    }
788
    
789
    /**
790
     * Return find lazy loading config for count
791
     * @return array|string
792
     */
793
    protected function getFindCount($find = null)
794
    {
795
        $find ??= $this->getFind();
796
        if (isset($find['limit'])) {
797
            unset($find['limit']);
798
        }
799
        if (isset($find['offset'])) {
800
            unset($find['offset']);
801
        }
802
//        if (isset($find['group'])) {
803
//            unset($find['group']);
804
//        }
805
        
806
        return array_filter($find);
807
    }
808
    
809
    /**
810
     * @param string $key
811
     * @param string[]|string|null $filters
812
     * @param string|null $default
813
     * @param array|null $params
814
     *
815
     * @return string[]|string|null
816
     */
817
    public function getParam(string $key, $filters = null, string $default = null, array $params = null)
818
    {
819
        $params ??= $this->getParams();
820
        
821
        return $this->filter->sanitize($params[$key] ?? $this->dispatcher->getParam($key, $filters, $default), $filters);
822
    }
823
    
824
    /**
825
     * Get parameters from
826
     * - JsonRawBody, post, put or get
827
     * @return mixed
828
     */
829
    protected function getParams(array $filters = null)
830
    {
831
        /** @var Request $request */
832
        $request = $this->request;
833
        
834
        if (!empty($filters)) {
835
            foreach ($filters as $filter) {
836
                $request->setParameterFilters($filter['name'], $filter['filters'], $filter['scope']);
837
            }
838
        }
839
840
//        $params = empty($request->getRawBody()) ? [] : $request->getJsonRawBody(true); // @TODO handle this differently
841
        return array_merge_recursive(
842
            $request->getFilteredQuery(), // $_GET
843
            $request->getFilteredPut(), // $_PUT
844
            $request->getFilteredPost(), // $_POST
845
        );
846
    }
847
    
848
    /**
849
     * Get Single from ID and Model Name
850
     *
851
     * @param string|int|null $id
852
     * @param string|null $modelName
853
     * @param string|array|null $with
854
     *
855
     * @return bool|Resultset
856
     */
857
    public function getSingle($id = null, $modelName = null, $with = [], $find = null, $appendCondition = true)
858
    {
859
        $id ??= (int)$this->getParam('id', 'int');
860
        $modelName ??= $this->getModelClassName();
861
        $with ??= $this->getWith();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWith() targeting Zemit\Mvc\Controller\Model::getWith() 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...
862
        $find ??= $this->getFind();
863
        $condition = '[' . $modelName . '].[id] = ' . (int)$id;
864
        if ($appendCondition) {
865
            $find['conditions'] .= (empty($find['conditions']) ? null : ' and ') . $condition;
866
        }
867
        else {
868
            $find['bind'] = [];
869
            $find['bindTypes'] = [];
870
            $find['conditions'] = $condition;
871
        }
872
        
873
        return $id ? $modelName::findFirstWith($with ?? [], $find ?? []) : false;
0 ignored issues
show
Bug introduced by
The method findFirstWith() does not exist on null. ( Ignorable by Annotation )

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

873
        return $id ? $modelName::/** @scrutinizer ignore-call */ findFirstWith($with ?? [], $find ?? []) : 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...
874
    }
875
    
876
    /**
877
     * Saving model automagically
878
     *
879
     * Note:
880
     * If a newly created entity can't be retrieved using the ->getSingle
881
     * method after it's creation, the entity will be returned directly
882
     *
883
     * @TODO Support Composite Primary Key
884
     *
885
     * @param null|int|string $id
886
     * @param null|\Zemit\Mvc\Model $entity
887
     * @param null|mixed $post
888
     * @param null|string $modelName
889
     * @param null|array $whiteList
890
     * @param null|array $columnMap
891
     * @param null|array $with
892
     *
893
     * @return array
894
     */
895
    protected function save($id = null, $entity = null, $post = null, $modelName = null, $whiteList = null, $columnMap = null, $with = null)
896
    {
897
        $single = false;
898
        $retList = [];
899
        
900
        // Get the model name to play with
901
        $modelName ??= $this->getModelClassName();
902
        $post ??= $this->getParams();
903
        $whiteList ??= $this->getWhiteList();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWhiteList() targeting Zemit\Mvc\Controller\Model::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...
904
        $columnMap ??= $this->getColumnMap();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getColumnMap() targeting Zemit\Mvc\Controller\Model::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...
905
        $with ??= $this->getWith();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getWith() targeting Zemit\Mvc\Controller\Model::getWith() 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...
906
        $id = (int)$id;
907
        
908
        // Check if multi-d post
909
        if (!empty($id) || !isset($post[0]) || !is_array($post[0])) {
910
            $single = true;
911
            $post = [$post];
912
        }
913
        
914
        // Save each posts
915
        foreach ($post as $key => $singlePost) {
916
            $ret = [];
917
            
918
            // @todo see if we should remove this earlier
919
            if (isset($singlePost['_url'])) {
920
                unset($singlePost['_url']);
921
            }
922
            
923
            $singlePostId = (!$single || empty($id)) ? $this->getParam('id', 'int', $this->getParam('int', 'int', null)) : $id;
0 ignored issues
show
Bug introduced by
It seems like $this->getParam('int', 'int', null) can also be of type string[]; however, parameter $default of Zemit\Mvc\Controller\Model::getParam() does only seem to accept null|string, 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

923
            $singlePostId = (!$single || empty($id)) ? $this->getParam('id', 'int', /** @scrutinizer ignore-type */ $this->getParam('int', 'int', null)) : $id;
Loading history...
924
            unset($singlePost['id']);
925
            
926
            /** @var \Zemit\Mvc\Model $singlePostEntity */
927
            $singlePostEntity = (!$single || !isset($entity)) ? $this->getSingle($singlePostId, $modelName) : $entity;
0 ignored issues
show
Bug introduced by
It seems like $singlePostId can also be of type string[]; however, parameter $id of Zemit\Mvc\Controller\Model::getSingle() does only seem to accept integer|null|string, 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

927
            $singlePostEntity = (!$single || !isset($entity)) ? $this->getSingle(/** @scrutinizer ignore-type */ $singlePostId, $modelName) : $entity;
Loading history...
928
            
929
            // Create entity if not exists
930
            if (!$singlePostEntity && empty($singlePostId)) {
931
                $singlePostEntity = new $modelName();
932
            }
933
            
934
            if (!$singlePostEntity) {
935
                $ret = [
936
                    'saved' => false,
937
                    'messages' => [new Message('Entity id `' . $singlePostId . '` not found.', $modelName, 'NotFound', 404)],
938
                    'model' => $modelName,
939
                    'source' => (new $modelName)->getSource(),
940
                ];
941
            }
942
            else {
943
                // assign & save
944
                $singlePostEntity->assign($singlePost, $whiteList, $columnMap);
945
                $ret = $this->saveEntity($singlePostEntity);
946
                
947
                // refetch & expose
948
                $fetch = $this->getSingle($singlePostEntity->getId(), $modelName, $with);
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Zemit\Mvc\Model. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

948
                $fetch = $this->getSingle($singlePostEntity->/** @scrutinizer ignore-call */ getId(), $modelName, $with);
Loading history...
949
                $ret[$single ? 'single' : 'list'] = $fetch ? $fetch->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

949
                $ret[$single ? 'single' : 'list'] = $fetch ? $fetch->/** @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\Model::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...
950
            }
951
            
952
            $retList [] = $ret;
953
        }
954
        
955
        return $single ? $retList[0] : $retList;
956
    }
957
    
958
    /**
959
     * @param $single
960
     *
961
     * @return void
962
     */
963
    protected function saveEntity($entity) : array
964
    {
965
        $ret = [];
966
        $ret['saved'] = $entity->save();
967
        $ret['messages'] = $entity->getMessages();
968
        $ret['model'] = get_class($entity);
969
        $ret['source'] = $entity->getSource();
970
        $ret['entity'] = $entity->expose($this->getExpose());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getExpose() targeting Zemit\Mvc\Controller\Model::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...
971
        return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret returns the type array which is incompatible with the documented return type void.
Loading history...
972
    }
973
    
974
    /**
975
     * Try to find the appropriate model from the current controller name
976
     *
977
     * @param ?string $controllerName
978
     * @param ?array $namespaces
979
     * @param string $needle
980
     *
981
     * @return string|null
982
     */
983
    public function getModelNameFromController(string $controllerName = null, array $namespaces = null, string $needle = 'Models'): ?string
984
    {
985
        $controllerName ??= $this->dispatcher->getControllerName() ?? '';
986
        $namespaces ??= $this->loader->getNamespaces() ?? [];
987
        
988
        $model = ucfirst(Text::camelize(Text::uncamelize($controllerName)));
989
        if (!class_exists($model)) {
990
            foreach ($namespaces as $namespace => $path) {
991
                $possibleModel = $namespace . '\\' . $model;
992
                if (strpos($namespace, $needle) !== false && class_exists($possibleModel)) {
993
                    $model = $possibleModel;
994
                }
995
            }
996
        }
997
        
998
        return class_exists($model) && new $model() instanceof ModelInterface ? $model : null;
999
    }
1000
    
1001
    /**
1002
     * Get message from list of entities
1003
     *
1004
     * @param $list Resultset|\Phalcon\Mvc\Model
1005
     *
1006
     * @return array|bool
1007
     * @deprecated
1008
     *
1009
     */
1010
    public function getRestMessages($list = null)
1011
    {
1012
        if (!is_array($list)) {
1013
            $list = [$list];
1014
        }
1015
        
1016
        $ret = [];
1017
        
1018
        foreach ($list as $single) {
1019
            
1020
            if ($single) {
1021
                
1022
                /** @var Messages $validations */
1023
                $messages = $single instanceof Message ? $list : $single->getMessages();
1024
                
1025
                if ($messages && (is_array($messages) || $messages instanceof \Traversable)) {
1026
                    
1027
                    foreach ($messages as $message) {
1028
                        
1029
                        $validationFields = $message->getField();
1030
                        
1031
                        if (!is_array($validationFields)) {
1032
                            $validationFields = [$validationFields];
1033
                        }
1034
                        
1035
                        foreach ($validationFields as $validationField) {
1036
                            
1037
                            if (empty($ret[$validationField])) {
1038
                                $ret[$validationField] = [];
1039
                            }
1040
                            
1041
                            $ret[$validationField][] = [
1042
                                'field' => $message->getField(),
1043
                                'code' => $message->getCode(),
1044
                                'type' => $message->getType(),
1045
                                'message' => $message->getMessage(),
1046
                                'metaData' => $message->getMetaData(),
1047
                            ];
1048
                        }
1049
                    }
1050
                }
1051
            }
1052
        }
1053
        
1054
        return $ret ? : false;
1055
    }
1056
}
1057