Passed
Push — master ( 3bb5db...959b11 )
by Julien
16:17
created

Model::getSearchCondition()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 16
c 2
b 1
f 0
dl 0
loc 31
ccs 0
cts 17
cp 0
rs 8.8333
cc 7
nc 10
nop 0
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 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 $input 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 expose definition
129
     *
130
     * @return null|array
131
     */
132
    protected function getExpose()
133
    {
134
        return null;
135
    }
136
    
137
    /**
138
     * Get join definition
139
     *
140
     * @return null|array
141
     */
142
    protected function getJoins()
143
    {
144
        return null;
145
    }
146
    
147
    /**
148
     * Get the order definition
149
     *
150
     * @return null|array
151
     */
152
    protected function getOrder()
153
    {
154
        return $this->getParamExplodeArrayMapFilter('order');
155
    }
156
    
157
    /**
158
     * Get the current limit value
159
     *
160
     * @return null|int Default: 1000
161
     */
162
    protected function getLimit(): int
163
    {
164
        return (int)$this->getParam('limit', 'int', 1000);
165
    }
166
    
167
    /**
168
     * Get the current offset value
169
     *
170
     * @return null|int Default: 0
171
     */
172
    protected function getOffset(): int
173
    {
174
        return (int)$this->getParam('offset', 'int', 0);
175
    }
176
    
177
    /**
178
     * Get group
179
     * - Automatically group by ID by default if nothing else is provided
180
     * - This will fix multiple single records being returned for the same model with joins
181
     *
182
     * @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...
183
     */
184
    protected function getGroup()
185
    {
186
        $group = $this->getParamExplodeArrayMapFilter('group');
187
        
188
        // Fix for joins, automatically append grouping if none provided
189
        $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...
190
        if (empty($group) && !empty($join)) {
191
            $group = $this->appendModelName('id');
192
        }
193
        
194
        return $group;
195
    }
196
    
197
    /**
198
     * Get distinct
199
     * @TODO see how to implement this, maybe an action itself
200
     *
201
     * @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...
202
     */
203
    protected function getDistinct()
204
    {
205
        return $this->getParamExplodeArrayMapFilter('distinct');
206
    }
207
    
208
    /**
209
     * Get columns
210
     * @TODO see how to implement this
211
     *
212
     * @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...
213
     */
214
    protected function getColumns()
215
    {
216
        return $this->getParam('columns', 'string');
217
    }
218
    
219
    /**
220
     * Return the whitelisted role list for the current model
221
     *
222
     * @return string[] By default will return dev and admin role
223
     */
224
    protected function getRoleList() {
225
        return ['dev', 'admin'];
226
    }
227
    
228
    /**
229
     * Get Search condition
230
     *
231
     * @return string Default: deleted = 0
232
     */
233
    protected function getSearchCondition()
234
    {
235
        $conditions = [];
236
        
237
        $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 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

237
        $searchList = array_values(array_filter(array_unique(explode(' ', /** @scrutinizer ignore-type */ $this->getParam('search', 'string')))));
Loading history...
238
        
239
        foreach ($searchList as $searchTerm) {
240
            $orConditions = [];
241
            $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...
242
            if ($searchWhiteList) {
243
                foreach ($searchWhiteList as $whiteList) {
0 ignored issues
show
Bug introduced by
The expression $searchWhiteList of type void is not traversable.
Loading history...
244
                    
245
                    // Multidimensional arrays not supported yet
246
                    // @todo support this properly
247
                    if (is_array($whiteList)) {
248
                        continue;
249
                    }
250
                    
251
                    $searchTermBinding = '_' . uniqid() . '_';
252
                    $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

252
                    $orConditions [] = /** @scrutinizer ignore-type */ $this->appendModelName($whiteList) . " like :$searchTermBinding:";
Loading history...
253
                    $this->setBind([$searchTermBinding => '%' . $searchTerm . '%']);
254
                    $this->setBindTypes([$searchTermBinding => Column::BIND_PARAM_STR]);
255
                }
256
            }
257
            
258
            if (!empty($orConditions)) {
259
                $conditions [] = '(' . implode(' or ', $orConditions) . ')';
260
            }
261
        }
262
        
263
        return empty($conditions) ? null : '(' . implode(' and ', $conditions) . ')';
264
    }
265
    
266
    /**
267
     * Get Soft delete condition
268
     *
269
     * @return string Default: deleted = 0
270
     */
271
    protected function getSoftDeleteCondition()
272
    {
273
        return '[' . $this->getModelName() . '].[deleted] = 0';
0 ignored issues
show
Deprecated Code introduced by
The function Zemit\Mvc\Controller\Model::getModelName() has been deprecated: change to getModelClassName() instead ( Ignorable by Annotation )

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

273
        return '[' . /** @scrutinizer ignore-deprecated */ $this->getModelName() . '].[deleted] = 0';

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
274
    }
275
    
276
    /**
277
     * @param $field
278
     * @param string $sanitizer
279
     * @param string $glue
280
     *
281
     * @return array|string[]
282
     */
283
    public function getParamExplodeArrayMapFilter($field, $sanitizer = 'string', $glue = ',')
284
    {
285
        $filter = $this->filter;
286
        $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...
287
            return $this->appendModelName(trim($filter->sanitize($e, $sanitizer)));
288
        }, 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 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
        }, explode($glue, /** @scrutinizer ignore-type */ $this->getParam($field, $sanitizer))));
Loading history...
289
        
290
        return empty($ret) ? null : $ret;
291
    }
292
    
293
    /**
294
     * Set the variables to bind
295
     *
296
     * @param array $bind Variable bind to merge or replace
297
     * @param bool $replace Pass true to replace the entire bind set
298
     */
299
    public function setBind(array $bind = [], bool $replace = false)
300
    {
301
        $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 $array1 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

301
        $this->_bind = $replace ? $bind : array_merge(/** @scrutinizer ignore-type */ $this->getBind(), $bind);
Loading history...
302
    }
303
    
304
    /**
305
     * Get the current bind
306
     * key => value
307
     *
308
     * @return array|null
309
     */
310
    public function getBind()
311
    {
312
        return $this->_bind ?? null;
313
    }
314
    
315
    /**
316
     * Set the variables types to bind
317
     *
318
     * @param array $bindTypes Variable bind types to merge or replace
319
     * @param bool $replace Pass true to replace the entire bind type set
320
     */
321
    public function setBindTypes(array $bindTypes = [], bool $replace = false)
322
    {
323
        $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 $array1 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

323
        $this->_bindTypes = $replace ? $bindTypes : array_merge(/** @scrutinizer ignore-type */ $this->getBindTypes(), $bindTypes);
Loading history...
324
    }
325
    
326
    /**
327
     * Get the current bind types
328
     *
329
     * @return array|null
330
     */
331
    public function getBindTypes()
332
    {
333
        return $this->_bindTypes ?? null;
334
    }
335
    
336
    /**
337
     * Get Created By Condition
338
     *
339
     * @param string[] $columns
340
     * @param Identity|null $identity
341
     * @param string[]|null $roleList
342
     *
343
     * @return null
344
     *
345
     * @return string|null
346
     */
347
    protected function getIdentityCondition(array $columns = null, Identity $identity = null, $roleList = null)
348
    {
349
        $identity ??= $this->identity ?? false;
350
        $roleList ??= $this->getRoleList();
351
        $modelName = $this->getModelClassName();
352
        
353
        if ($modelName && $identity && !$identity->hasRole($roleList)) {
354
            $ret = [];
355
            
356
            $columns ??= [
357
                'createdBy',
358
                'ownedBy',
359
                'userId',
360
            ];
361
            
362
            foreach ($columns as $column) {
363
                if (!property_exists($modelName, $column)) {
364
                    continue;
365
                }
366
                
367
                $field = str_contains($column, '.') ? $column : $modelName . '.' . $column;
368
                $field = '[' . str_replace('.', '].[', $field) . ']';
369
                
370
                $this->setBind([$column => (int)$identity->getUserId()]);
371
                $this->setBindTypes([$column => Column::BIND_PARAM_INT]);
372
                $ret [] = $field . ' = :' . $column . ':';
373
            }
374
            
375
            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...
376
        }
377
        
378
        return null;
379
    }
380
    
381
    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...
382
    {
383
        $func = function($item) use (&$func, &$callback) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
384
            return is_array($item) ? array_map($func, $item) : call_user_func($callback, $item);
385
        };
386
        
387
        return array_map($func, $array);
388
    }
389
    
390
    /**
391
     * Get Filter Condition
392
     *
393
     * @param array|null $filters
394
     * @param array|null $whiteList
395
     * @param bool $or
396
     *
397
     * @return string|null Return the generated query
398
     * @throws \Exception Throw an exception if the field property is not valid
399
     * @todo escape fields properly
400
     *
401
     */
402
    protected function getFilterCondition(array $filters = null, array $whiteList = null, $or = false)
403
    {
404
        $filters ??= $this->getParam('filters');
405
        $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...
406
        $whiteList = $this->getFlatWhiteList($whiteList);
407
        $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...
408
        
409
        // No filter, no query
410
        if (empty($filters)) {
411
            return null;
412
        }
413
        
414
        $query = [];
415
        foreach ($filters as $filter) {
416
            $field = $this->filter->sanitize($filter['field'] ?? null, ['string', 'trim']);
417
            $lowercaseField = mb_strtolower($field);
418
            
419
            // whiteList on filter condition
420
            if (is_null($whiteList) || !in_array($lowercaseField, $lowercaseWhiteList, true)) {
421
                throw new \Exception('Not allowed to filter using the following field: `' . $field . '`', 403);
422
                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...
423
            }
424
            
425
            if (!empty($field)) {
426
                $uniqid = substr(md5(json_encode($filter)), 0, 6);
427
//                $queryField = '_' . uniqid($uniqid . '_field_') . '_';
428
                $queryValue = '_' . uniqid($uniqid . '_value_') . '_';
429
                $queryOperator = strtolower($filter['operator']);
430
                switch($queryOperator) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after SWITCH keyword; 0 found
Loading history...
431
                    case '=': // Equal operator
432
                    case '!=': // Not equal operator
433
                    case '<>': // Not equal operator
434
                    case '>': // Greater than operator
435
                    case '>=': // Greater than or equal operator
436
                    case '<': // Less than or equal operator
437
                    case '<=': // Less than or equal operator
438
                    case '<=>': // NULL-safe equal to operator
439
                    case 'in': // Whether a value is within a set of values
440
                    case 'not in': // Whether a value is not within a set of values
441
                    case 'like': // Simple pattern matching
442
                    case 'not like': // Negation of simple pattern matching
443
                    case 'between': // Whether a value is within a range of values
444
                    case 'not between': // Whether a value is not within a range of values
445
                    case 'is': // Test a value against a boolean
446
                    case 'is not': // Test a value against a boolean
447
                    case 'is null': // NULL value test
448
                    case 'is not null': // NOT NULL value test
449
                    case 'is false': // Test a value against a boolean
450
                    case 'is not false': // // Test a value against a boolean
451
                    case 'is true': // // Test a value against a boolean
452
                    case 'is not true': // // Test a value against a boolean
453
                        break;
454
                    default:
455
                        throw new \Exception('Not allowed to filter using the following operator: `' . $queryOperator . '`', 403);
456
                        break;
457
                }
458
                
459
                $bind = [];
460
                $bindType = [];
461
462
//                $bind[$queryField] = $filter['field'];
463
//                $bindType[$queryField] = Column::BIND_PARAM_STR;
464
//                $queryFieldBinder = ':' . $queryField . ':';
465
//                $queryFieldBinder = '{' . $queryField . '}';
466
                
467
                // Add the current model name by default
468
                $field = $this->appendModelName($field);
469
                
470
                $queryFieldBinder = $field;
471
                $queryValueBinder = ':' . $queryValue . ':';
472
                if (isset($filter['value'])) {
473
                    // special for between and not between
474
                    if (in_array($queryOperator, ['between', 'not between'])) {
475
                        $queryValue0 = '_' . uniqid($uniqid . '_value_') . '_';
476
                        $queryValue1 = '_' . uniqid($uniqid . '_value_') . '_';
477
                        $bind[$queryValue0] = $filter['value'][0];
478
                        $bind[$queryValue1] = $filter['value'][1];
479
                        $bindType[$queryValue0] = Column::BIND_PARAM_STR;
480
                        $bindType[$queryValue1] = Column::BIND_PARAM_STR;
481
                        $query [] = "$queryFieldBinder $queryOperator :$queryValue0: and :$queryValue1:";
482
                    }
483
                    else {
484
                        $bind[$queryValue] = $filter['value'];
485
                        if (is_string($filter['value'])) {
486
                            $bindType[$queryValue] = Column::BIND_PARAM_STR;
487
                        }
488
                        else if (is_int($filter['value'])) {
489
                            $bindType[$queryValue] = Column::BIND_PARAM_INT;
490
                        }
491
                        else if (is_bool($filter['value'])) {
492
                            $bindType[$queryValue] = Column::BIND_PARAM_BOOL;
493
                        }
494
                        else if (is_float($filter['value'])) {
495
                            $bindType[$queryValue] = Column::BIND_PARAM_DECIMAL;
496
                        }
497
                        else if (is_double($filter['value'])) {
498
                            $bindType[$queryValue] = Column::BIND_PARAM_DECIMAL;
499
                        }
500
                        else if (is_array($filter['value'])) {
501
                            $queryValueBinder = '({' . $queryValue . ':array})';
502
                            $bindType[$queryValue] = Column::BIND_PARAM_STR;
503
                        }
504
                        else {
505
                            $bindType[$queryValue] = Column::BIND_PARAM_NULL;
506
                        }
507
                        $query [] = "$queryFieldBinder $queryOperator $queryValueBinder";
508
                    }
509
                }
510
                else {
511
                    $query [] = "$queryFieldBinder $queryOperator";
512
                }
513
                
514
                $this->setBind($bind);
515
                $this->setBindTypes($bindType);
516
            }
517
            else {
518
                if (is_array($filter) || $filter instanceof \Traversable) {
519
                    $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

519
                    $query [] = $this->getFilterCondition(/** @scrutinizer ignore-type */ $filter, $whiteList, !$or);
Loading history...
520
                }
521
                else {
522
                    throw new \Exception('A valid field property is required.', 400);
523
                }
524
            }
525
        }
526
        
527
        return empty($query) ? null : '(' . implode($or ? ' or ' : ' and ', $query) . ')';
528
    }
529
    
530
    /**
531
     * Append the current model name alias to the field
532
     * So: field -> [Alias].[field]
533
     *
534
     * @param string|array $field
535
     * @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...
536
     *
537
     * @return array|string
538
     */
539
    public function appendModelName($field, $modelName = null)
540
    {
541
        $modelName ??= $this->getModelClassName();
542
        
543
        if (empty($field)) {
544
            return $field;
545
        }
546
        
547
        if (is_string($field)) {
548
            // Add the current model name by default
549
            $explode = explode(' ', $field);
550
            if (!str_contains($field, '.')) {
551
                $field = trim('[' . $modelName . '].[' . array_shift($explode) . '] ' . implode(' ', $explode));
552
            }
553
            else if (!str_contains($field, ']') && !str_contains($field, '[')) {
554
                $field = trim('[' . implode('].[', explode('.', array_shift($explode))) . ']' . implode(' ', $explode));
555
            }
556
        }
557
        else if (is_array($field)) {
0 ignored issues
show
introduced by
The condition is_array($field) is always true.
Loading history...
558
            foreach ($field as $fieldKey => $fieldValue) {
559
                $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

559
                $field[$fieldKey] = $this->appendModelName($fieldValue, /** @scrutinizer ignore-type */ $modelName);
Loading history...
560
            }
561
        }
562
        
563
        return $field;
564
    }
565
    
566
    /**
567
     * Get Permission Condition
568
     *
569
     * @return null
570
     */
571
    protected function getPermissionCondition($type = null, $identity = null)
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

571
    protected function getPermissionCondition(/** @scrutinizer ignore-unused */ $type = null, $identity = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $identity is not used and could be removed. ( Ignorable by Annotation )

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

571
    protected function getPermissionCondition($type = null, /** @scrutinizer ignore-unused */ $identity = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
572
    {
573
        return null;
574
    }
575
    
576
    /**
577
     * Get all conditions
578
     *
579
     * @return string
580
     * @throws \Exception
581
     */
582
    protected function getConditions()
583
    {
584
        $conditions = array_values(array_unique(array_filter([
585
            $this->getSoftDeleteCondition(),
586
            $this->getIdentityCondition(),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getIdentityCondition() targeting Zemit\Mvc\Controller\Model::getIdentityCondition() 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...
587
            $this->getFilterCondition(),
588
            $this->getSearchCondition(),
589
            $this->getPermissionCondition(),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getPermissionCondition() targeting Zemit\Mvc\Controller\Mod...etPermissionCondition() 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...
590
        ])));
591
        
592
        return '(' . implode(') and (', $conditions) . ')';
593
    }
594
    
595
    /**
596
     * Get having conditions
597
     */
598
    public function getHaving()
599
    {
600
        return null;
601
    }
602
    
603
    /**
604
     * Get a cache key from params
605
     *
606
     * @param array|null $params
607
     *
608
     * @return string|null
609
     */
610
    public function getCacheKey(?array $params = null): ?string
611
    {
612
        $params ??= $this->getParams();
613
        
614
        return Slug::generate(json_encode($params, JSON_UNESCAPED_SLASHES));
615
    }
616
    
617
    /**
618
     * Get cache setting
619
     *
620
     * @param array|null $params
621
     *
622
     * @return array|null
623
     */
624
    public function getCache(?array $params = null)
625
    {
626
        $params ??= $this->getParams();
627
        
628
        if (!empty($params['cache'])) {
629
            return [
630
                'lifetime' => (int)$params['cache'],
631
                'key' => $this->getCacheKey($params),
632
            ];
633
        }
634
        
635
        return null;
636
    }
637
    
638
    
639
    /**
640
     * Get requested content type
641
     * - Default will return csv
642
     *
643
     * @param array|null $params
644
     *
645
     * @return string
646
     * @throws \Exception
647
     */
648
    public function getContentType(?array $params = null)
649
    {
650
        $params ??= $this->getParams();
651
        
652
        $contentType = strtolower($params['contentType'] ?? $params['content-type'] ?? 'json');
653
        
654
        switch($contentType) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after SWITCH keyword; 0 found
Loading history...
655
            case 'html':
656
            case 'text/html':
657
            case 'application/html':
658
                // html not supported yet
659
                break;
660
            case 'xml':
661
            case 'text/xml':
662
            case 'application/xml':
663
                // xml not supported yet
664
                break;
665
            case 'text':
666
            case 'text/plain':
667
                // plain text not supported yet
668
                break;
669
            case 'json':
670
            case 'text/json':
671
            case 'application/json':
672
                return 'json';
673
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
674
            case 'csv':
675
            case 'text/csv':
676
                return 'csv';
677
                break;
678
            case 'xlsx':
679
            case 'application/xlsx':
680
            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
681
                return 'xlsx';
682
                break;
683
            case 'xls':
684
            case 'application/vnd.ms-excel':
685
                // old xls not supported yet
686
                break;
687
        }
688
        
689
        throw new \Exception('`' . $contentType . '` is not supported.', 400);
690
    }
691
    
692
    /**
693
     * Get find definition
694
     *
695
     * @return array
696
     * @throws \Exception
697
     */
698
    protected function getFind()
699
    {
700
        $find = [];
701
        $find['conditions'] = $this->getConditions();
702
        $find['bind'] = $this->getBind();
703
        $find['bindTypes'] = $this->getBindTypes();
704
        $find['limit'] = $this->getLimit();
705
        $find['offset'] = $this->getOffset();
706
        $find['order'] = $this->getOrder();
707
        $find['columns'] = $this->getColumns();
708
        $find['distinct'] = $this->getDistinct();
709
        $find['joins'] = $this->getJoins();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $find['joins'] 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...
710
        $find['group'] = $this->getGroup();
711
        $find['having'] = $this->getHaving();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $find['having'] is correct as $this->getHaving() targeting Zemit\Mvc\Controller\Model::getHaving() 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...
712
        $find['cache'] = $this->getCache();
713
        
714
        // fix for grouping by multiple fields, phalcon only allow string here
715
        foreach (['distinct', 'group'] as $findKey) {
716
            if (isset($find[$findKey]) && is_array($find[$findKey])) {
717
                $find[$findKey] = implode(', ', $find[$findKey]);
718
            }
719
        }
720
        
721
        return array_filter($find);
722
    }
723
    
724
    /**
725
     * Return find lazy loading config for count
726
     * @return array|string
727
     */
728
    protected function getFindCount($find = null)
729
    {
730
        $find ??= $this->getFind();
731
        if (isset($find['limit'])) {
732
            unset($find['limit']);
733
        }
734
        if (isset($find['offset'])) {
735
            unset($find['offset']);
736
        }
737
//        if (isset($find['group'])) {
738
//            unset($find['group']);
739
//        }
740
        
741
        return array_filter($find);
742
    }
743
    
744
    /**
745
     * @param string $key
746
     * @param string|null $default
747
     * @param array|null $params
748
     *
749
     * @return string[]|string|null
750
     */
751
    public function getParam(string $key, string $filters = null, string $default = null, array $params = null)
752
    {
753
        $params ??= $this->getParams();
754
        
755
        return $this->filter->sanitize($params[$key] ?? $this->dispatcher->getParam($key, $filters, $default), $filters);
756
    }
757
    
758
    /**
759
     * Get parameters from
760
     * - JsonRawBody, post, put or get
761
     * @return mixed
762
     */
763
    protected function getParams(array $filters = null)
764
    {
765
        /** @var Request $request */
766
        $request = $this->request;
767
        
768
        if (!empty($filters)) {
769
            foreach ($filters as $filter) {
770
                $request->setParameterFilters($filter['name'], $filter['filters'], $filter['scope']);
771
            }
772
        }
773
774
//        $params = empty($request->getRawBody()) ? [] : $request->getJsonRawBody(true); // @TODO handle this differently
775
        return array_merge_recursive(
776
            $request->getFilteredQuery(), // $_GET
777
            $request->getFilteredPut(), // $_PUT
778
            $request->getFilteredPost(), // $_POST
779
        );
780
    }
781
    
782
    /**
783
     * Get Single from ID and Model Name
784
     *
785
     * @param string|int|null $id
786
     * @param string|null $modelName
787
     * @param string|array|null $with
788
     *
789
     * @return bool|Resultset
790
     */
791
    public function getSingle($id = null, $modelName = null, $with = [], $find = null, $appendCondition = true)
792
    {
793
        $id ??= (int)$this->getParam('id', 'int');
794
        $modelName ??= $this->getModelName();
0 ignored issues
show
Deprecated Code introduced by
The function Zemit\Mvc\Controller\Model::getModelName() has been deprecated: change to getModelClassName() instead ( Ignorable by Annotation )

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

794
        $modelName ??= /** @scrutinizer ignore-deprecated */ $this->getModelName();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
795
        $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...
796
        $find ??= $this->getFind();
797
        $condition = '[' . $modelName . '].[id] = ' . (int)$id;
798
        if ($appendCondition) {
799
            $find['conditions'] .= (empty($find['conditions']) ? null : ' and ') . $condition;
800
        }
801
        else {
802
            $find['bind'] = [];
803
            $find['bindTypes'] = [];
804
            $find['conditions'] = $condition;
805
        }
806
        
807
        
808
        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

808
        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...
809
    }
810
    
811
    /**
812
     * Saving model automagically
813
     *
814
     * Note:
815
     * If a newly created entity can't be retrieved using the ->getSingle
816
     * method after it's creation, the entity will be returned directly
817
     *
818
     * @TODO Support Composite Primary Key
819
     *
820
     * @param null|int|string $id
821
     * @param null|\Zemit\Mvc\Model $entity
822
     * @param null|mixed $post
823
     * @param null|string $modelName
824
     * @param null|array $whiteList
825
     * @param null|array $columnMap
826
     * @param null|array $with
827
     *
828
     * @return array
829
     */
830
    protected function save($id = null, $entity = null, $post = null, $modelName = null, $whiteList = null, $columnMap = null, $with = null)
831
    {
832
        $single = false;
833
        $retList = [];
834
        
835
        // Get the model name to play with
836
        $modelName ??= $this->getModelClassName();
837
        $post ??= $this->getParams();
838
        $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...
839
        $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...
840
        $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...
841
        $id = (int)$id;
842
        
843
        // Check if multi-d post
844
        if (!empty($id) || !isset($post[0]) || !is_array($post[0])) {
845
            $single = true;
846
            $post = [$post];
847
        }
848
        
849
        // Save each posts
850
        foreach ($post as $key => $singlePost) {
851
            $ret = [];
852
            
853
            // @todo see if we should remove this earlier
854
            if (isset($singlePost['_url'])) {
855
                unset($singlePost['_url']);
856
            }
857
            
858
            $singlePostId = (!$single || empty($id)) ? $this->getParam('id', 'number', $this->getParam('int', 'number', null)) : $id;
0 ignored issues
show
Bug introduced by
It seems like $this->getParam('int', 'number', 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

858
            $singlePostId = (!$single || empty($id)) ? $this->getParam('id', 'number', /** @scrutinizer ignore-type */ $this->getParam('int', 'number', null)) : $id;
Loading history...
859
            unset($singlePost['id']);
860
            
861
            /** @var \Zemit\Mvc\Model $singlePostEntity */
862
            $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

862
            $singlePostEntity = (!$single || !isset($entity)) ? $this->getSingle(/** @scrutinizer ignore-type */ $singlePostId, $modelName) : $entity;
Loading history...
863
            
864
            // Create entity if not exists
865
            if (!$singlePostEntity && empty($singlePostId)) {
866
                $singlePostEntity = new $modelName();
867
            }
868
            
869
            if (!$singlePostEntity) {
870
                $ret = [
871
                    'saved' => false,
872
                    'messages' => [new Message('Entity id `' . $singlePostId . '` not found.', $modelName, 'NotFound', 404)],
873
                    'model' => $modelName,
874
                    'source' => (new $modelName)->getSource(),
875
                ];
876
            }
877
            else {
878
                $singlePostEntity->assign($singlePost, $whiteList, $columnMap);
879
                $ret['saved'] = $singlePostEntity->save();
880
                $ret['messages'] = $singlePostEntity->getMessages();
881
                $ret['model'] = get_class($singlePostEntity);
882
                $ret['source'] = $singlePostEntity->getSource();
883
                $ret['entity'] = $singlePostEntity->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...
884
                $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

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

885
                $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...
886
            }
887
            
888
            $retList [] = $ret;
889
        }
890
        
891
        return $single ? $retList[0] : $retList;
892
    }
893
    
894
    /**
895
     * Try to find the appropriate model from the current controller name
896
     *
897
     * @param string $controllerName
898
     * @param array $namespaces
899
     * @param string $needle
900
     *
901
     * @return string|null
902
     */
903
    public function getModelNameFromController(string $controllerName = null, array $namespaces = null, string $needle = 'Models'): ?string
904
    {
905
        $controllerName ??= $this->dispatcher->getControllerName() ?? '';
906
        $namespaces ??= $this->loader->getNamespaces() ?? [];
907
        
908
        $model = ucfirst(Text::camelize(Text::uncamelize($controllerName)));
909
        if (!class_exists($model)) {
910
            foreach ($namespaces as $namespace => $path) {
911
                $possibleModel = $namespace . '\\' . $model;
912
                if (strpos($namespace, $needle) !== false && class_exists($possibleModel)) {
913
                    $model = $possibleModel;
914
                }
915
            }
916
        }
917
        
918
        return class_exists($model) && new $model() instanceof ModelInterface ? $model : null;
919
    }
920
    
921
    /**
922
     * Get message from list of entities
923
     *
924
     * @param $list Resultset|\Phalcon\Mvc\Model
925
     *
926
     * @return array|bool
927
     * @deprecated
928
     *
929
     */
930
    public function getRestMessages($list = null)
931
    {
932
        if (!is_array($list)) {
933
            $list = [$list];
934
        }
935
        
936
        $ret = [];
937
        
938
        foreach ($list as $single) {
939
            
940
            if ($single) {
941
                
942
                /** @var Messages $validations */
943
                $messages = $single instanceof Message ? $list : $single->getMessages();
944
                
945
                if ($messages && (is_array($messages) || $messages instanceof \Traversable)) {
946
                    
947
                    foreach ($messages as $message) {
948
                        
949
                        $validationFields = $message->getField();
950
                        
951
                        if (!is_array($validationFields)) {
952
                            $validationFields = [$validationFields];
953
                        }
954
                        
955
                        foreach ($validationFields as $validationField) {
956
                            
957
                            if (empty($ret[$validationField])) {
958
                                $ret[$validationField] = [];
959
                            }
960
                            
961
                            $ret[$validationField][] = [
962
                                'field' => $message->getField(),
963
                                'code' => $message->getCode(),
964
                                'type' => $message->getType(),
965
                                'message' => $message->getMessage(),
966
                                'metaData' => $message->getMetaData(),
967
                            ];
968
                        }
969
                    }
970
                }
971
            }
972
        }
973
        
974
        return $ret ? : false;
975
    }
976
}
977