Test Failed
Pull Request — master (#3)
by
unknown
11:53
created

Model::getFindCount()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 14
ccs 0
cts 7
cp 0
rs 10
cc 3
nc 4
nop 1
crap 12
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 expose definition for a single entity
129
     *
130
     * @return null|array
131
     */
132
    protected function getExpose()
133
    {
134
        return null;
135
    }
136
    
137
    /**
138
     * Get expose definition for listing many entities
139
     *
140
     * @return null|array
141
     */
142
    protected function getListExpose()
143
    {
144
        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...
145
    }
146
    
147
    /**
148
     * Get expose definition for export
149
     *
150
     * @return null|array
151
     */
152
    protected function getExportExpose()
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 columns merge definition for export
159
     *
160
     * @return null|array
161
     */
162
    public function getExportMergeColum ()
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces before opening parenthesis; 1 found
Loading history...
Coding Style introduced by
Expected "function abc(...)"; found "function abc (...)"
Loading history...
163
    {
164
        return null;
165
    }
166
167
    /**
168
     * Get columns format field text definition for export
169
     *
170
     * @param array|null $params
171
     *
172
     * @return null|array
173
     */
174
    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...
Unused Code introduced by
The parameter $params 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

174
    public function getExportFormatFieldText (/** @scrutinizer ignore-unused */ ?array $params = 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...
175
        return null;
176
    }
177
    
178
    /**
179
     * Get join definition
180
     *
181
     * @return null|array
182
     */
183
    protected function getJoins()
184
    {
185
        return null;
186
    }
187
    
188
    /**
189
     * Get the order definition
190
     *
191
     * @return null|array
192
     */
193
    protected function getOrder()
194
    {
195
        return $this->getParamExplodeArrayMapFilter('order');
196
    }
197
    
198
    /**
199
     * Get the current limit value
200
     *
201
     * @return null|int Default: 1000
202
     */
203
    protected function getLimit(): int
204
    {
205
        return (int)$this->getParam('limit', 'int', 1000);
206
    }
207
    
208
    /**
209
     * Get the current offset value
210
     *
211
     * @return null|int Default: 0
212
     */
213
    protected function getOffset(): int
214
    {
215
        return (int)$this->getParam('offset', 'int', 0);
216
    }
217
    
218
    /**
219
     * Get group
220
     * - Automatically group by ID by default if nothing else is provided
221
     * - This will fix multiple single records being returned for the same model with joins
222
     *
223
     * @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...
224
     */
225
    protected function getGroup()
226
    {
227
        $group = $this->getParamExplodeArrayMapFilter('group');
228
        
229
        // Fix for joins, automatically append grouping if none provided
230
        $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...
231
        if (empty($group) && !empty($join)) {
232
            $group = $this->appendModelName('id');
233
        }
234
        
235
        return $group;
236
    }
237
    
238
    /**
239
     * Get distinct
240
     * @TODO see how to implement this, maybe an action itself
241
     *
242
     * @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...
243
     */
244
    protected function getDistinct()
245
    {
246
        return $this->getParamExplodeArrayMapFilter('distinct');
247
    }
248
    
249
    /**
250
     * Get columns
251
     * @TODO see how to implement this
252
     *
253
     * @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...
254
     */
255
    protected function getColumns()
256
    {
257
        return $this->getParam('columns', 'string');
258
    }
259
    
260
    /**
261
     * Return the whitelisted role list for the current model
262
     *
263
     * @return string[] By default will return dev and admin role
264
     */
265
    protected function getRoleList() {
266
        return ['dev', 'admin'];
267
    }
268
    
269
    /**
270
     * Get Search condition
271
     *
272
     * @return string Default: deleted = 0
273
     */
274
    protected function getSearchCondition()
275
    {
276
        $conditions = [];
277
        
278
        $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

278
        $searchList = array_values(array_filter(array_unique(explode(' ', /** @scrutinizer ignore-type */ $this->getParam('search', 'string')))));
Loading history...
279
        
280
        foreach ($searchList as $searchTerm) {
281
            $orConditions = [];
282
            $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...
283
            if ($searchWhiteList) {
284
                foreach ($searchWhiteList as $whiteList) {
0 ignored issues
show
Bug introduced by
The expression $searchWhiteList of type void is not traversable.
Loading history...
285
                    
286
                    // Multidimensional arrays not supported yet
287
                    // @todo support this properly
288
                    if (is_array($whiteList)) {
289
                        continue;
290
                    }
291
                    
292
                    $searchTermBinding = '_' . uniqid() . '_';
293
                    $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

293
                    $orConditions [] = /** @scrutinizer ignore-type */ $this->appendModelName($whiteList) . " like :$searchTermBinding:";
Loading history...
294
                    $this->setBind([$searchTermBinding => '%' . $searchTerm . '%']);
295
                    $this->setBindTypes([$searchTermBinding => Column::BIND_PARAM_STR]);
296
                }
297
            }
298
            
299
            if (!empty($orConditions)) {
300
                $conditions [] = '(' . implode(' or ', $orConditions) . ')';
301
            }
302
        }
303
        
304
        return empty($conditions) ? null : '(' . implode(' and ', $conditions) . ')';
305
    }
306
    
307
    /**
308
     * Get Soft delete condition
309
     *
310
     * @return string Default: deleted = 0
311
     */
312
    protected function getSoftDeleteCondition()
313
    {
314
        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

314
        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...
315
    }
316
    
317
    /**
318
     * @param $field
319
     * @param string $sanitizer
320
     * @param string $glue
321
     *
322
     * @return array|string[]
323
     */
324
    public function getParamExplodeArrayMapFilter($field, $sanitizer = 'string', $glue = ',')
325
    {
326
        $filter = $this->filter;
327
        $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...
328
            return $this->appendModelName(trim($filter->sanitize($e, $sanitizer)));
329
        }, 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

329
        }, explode($glue, /** @scrutinizer ignore-type */ $this->getParam($field, $sanitizer))));
Loading history...
330
        
331
        return empty($ret) ? null : $ret;
332
    }
333
    
334
    /**
335
     * Set the variables to bind
336
     *
337
     * @param array $bind Variable bind to merge or replace
338
     * @param bool $replace Pass true to replace the entire bind set
339
     */
340
    public function setBind(array $bind = [], bool $replace = false)
341
    {
342
        $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

342
        $this->_bind = $replace ? $bind : array_merge(/** @scrutinizer ignore-type */ $this->getBind(), $bind);
Loading history...
343
    }
344
    
345
    /**
346
     * Get the current bind
347
     * key => value
348
     *
349
     * @return array|null
350
     */
351
    public function getBind()
352
    {
353
        return $this->_bind ?? null;
354
    }
355
    
356
    /**
357
     * Set the variables types to bind
358
     *
359
     * @param array $bindTypes Variable bind types to merge or replace
360
     * @param bool $replace Pass true to replace the entire bind type set
361
     */
362
    public function setBindTypes(array $bindTypes = [], bool $replace = false)
363
    {
364
        $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

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

562
                    $query [] = $this->getFilterCondition(/** @scrutinizer ignore-type */ $filter, $whiteList, !$or);
Loading history...
563
                }
564
                else {
565
                    throw new \Exception('A valid field property is required.', 400);
566
                }
567
            }
568
        }
569
        
570
        return empty($query) ? null : '(' . implode($or ? ' or ' : ' and ', $query) . ')';
571
    }
572
    
573
    /**
574
     * Append the current model name alias to the field
575
     * So: field -> [Alias].[field]
576
     *
577
     * @param string|array $field
578
     * @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...
579
     *
580
     * @return array|string
581
     */
582
    public function appendModelName($field, $modelName = null)
583
    {
584
        $modelName ??= $this->getModelClassName();
585
        
586
        if (empty($field)) {
587
            return $field;
588
        }
589
        
590
        if (is_string($field)) {
591
            // Add the current model name by default
592
            $explode = explode(' ', $field);
593
            if (!strpos($field, '.') !== false) {
594
                $field = trim('[' . $modelName . '].[' . array_shift($explode) . '] ' . implode(' ', $explode));
595
            }
596
            else if (strpos($field, ']') === false && strpos($field, '[') === false) {
597
                $field = trim('[' . implode('].[', explode('.', array_shift($explode))) . ']' . implode(' ', $explode));
598
            }
599
        }
600
        else if (is_array($field)) {
0 ignored issues
show
introduced by
The condition is_array($field) is always true.
Loading history...
601
            foreach ($field as $fieldKey => $fieldValue) {
602
                $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

602
                $field[$fieldKey] = $this->appendModelName($fieldValue, /** @scrutinizer ignore-type */ $modelName);
Loading history...
603
            }
604
        }
605
        
606
        return $field;
607
    }
608
    
609
    /**
610
     * Get Permission Condition
611
     *
612
     * @return null
613
     */
614
    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

614
    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

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

851
        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...
852
    }
853
    
854
    /**
855
     * Saving model automagically
856
     *
857
     * Note:
858
     * If a newly created entity can't be retrieved using the ->getSingle
859
     * method after it's creation, the entity will be returned directly
860
     *
861
     * @TODO Support Composite Primary Key
862
     *
863
     * @param null|int|string $id
864
     * @param null|\Zemit\Mvc\Model $entity
865
     * @param null|mixed $post
866
     * @param null|string $modelName
867
     * @param null|array $whiteList
868
     * @param null|array $columnMap
869
     * @param null|array $with
870
     *
871
     * @return array
872
     */
873
    protected function save($id = null, $entity = null, $post = null, $modelName = null, $whiteList = null, $columnMap = null, $with = null)
874
    {
875
        $single = false;
876
        $retList = [];
877
        
878
        // Get the model name to play with
879
        $modelName ??= $this->getModelClassName();
880
        $post ??= $this->getParams();
881
        $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...
882
        $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...
883
        $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...
884
        $id = (int)$id;
885
        
886
        // Check if multi-d post
887
        if (!empty($id) || !isset($post[0]) || !is_array($post[0])) {
888
            $single = true;
889
            $post = [$post];
890
        }
891
        
892
        // Save each posts
893
        foreach ($post as $key => $singlePost) {
894
            $ret = [];
895
            
896
            // @todo see if we should remove this earlier
897
            if (isset($singlePost['_url'])) {
898
                unset($singlePost['_url']);
899
            }
900
            
901
            $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

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

905
            $singlePostEntity = (!$single || !isset($entity)) ? $this->getSingle(/** @scrutinizer ignore-type */ $singlePostId, $modelName) : $entity;
Loading history...
906
            
907
            // Create entity if not exists
908
            if (!$singlePostEntity && empty($singlePostId)) {
909
                $singlePostEntity = new $modelName();
910
            }
911
            
912
            if (!$singlePostEntity) {
913
                $ret = [
914
                    'saved' => false,
915
                    'messages' => [new Message('Entity id `' . $singlePostId . '` not found.', $modelName, 'NotFound', 404)],
916
                    'model' => $modelName,
917
                    'source' => (new $modelName)->getSource(),
918
                ];
919
            }
920
            else {
921
                $singlePostEntity->assign($singlePost, $whiteList, $columnMap);
922
                $ret['saved'] = $singlePostEntity->save();
923
                $ret['messages'] = $singlePostEntity->getMessages();
924
                $ret['model'] = get_class($singlePostEntity);
925
                $ret['source'] = $singlePostEntity->getSource();
926
                $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...
927
                $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

927
                $fetch = $this->getSingle($singlePostEntity->/** @scrutinizer ignore-call */ getId(), $modelName, $with);
Loading history...
928
                $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

928
                $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...
929
            }
930
            
931
            $retList [] = $ret;
932
        }
933
        
934
        return $single ? $retList[0] : $retList;
935
    }
936
    
937
    /**
938
     * Try to find the appropriate model from the current controller name
939
     *
940
     * @param string $controllerName
941
     * @param array $namespaces
942
     * @param string $needle
943
     *
944
     * @return string|null
945
     */
946
    public function getModelNameFromController(string $controllerName = null, array $namespaces = null, string $needle = 'Models'): ?string
947
    {
948
        $controllerName ??= $this->dispatcher->getControllerName() ?? '';
949
        $namespaces ??= $this->loader->getNamespaces() ?? [];
950
        
951
        $model = ucfirst(Text::camelize(Text::uncamelize($controllerName)));
952
        if (!class_exists($model)) {
953
            foreach ($namespaces as $namespace => $path) {
954
                $possibleModel = $namespace . '\\' . $model;
955
                if (strpos($namespace, $needle) !== false && class_exists($possibleModel)) {
956
                    $model = $possibleModel;
957
                }
958
            }
959
        }
960
        
961
        return class_exists($model) && new $model() instanceof ModelInterface ? $model : null;
962
    }
963
    
964
    /**
965
     * Get message from list of entities
966
     *
967
     * @param $list Resultset|\Phalcon\Mvc\Model
968
     *
969
     * @return array|bool
970
     * @deprecated
971
     *
972
     */
973
    public function getRestMessages($list = null)
974
    {
975
        if (!is_array($list)) {
976
            $list = [$list];
977
        }
978
        
979
        $ret = [];
980
        
981
        foreach ($list as $single) {
982
            
983
            if ($single) {
984
                
985
                /** @var Messages $validations */
986
                $messages = $single instanceof Message ? $list : $single->getMessages();
987
                
988
                if ($messages && (is_array($messages) || $messages instanceof \Traversable)) {
989
                    
990
                    foreach ($messages as $message) {
991
                        
992
                        $validationFields = $message->getField();
993
                        
994
                        if (!is_array($validationFields)) {
995
                            $validationFields = [$validationFields];
996
                        }
997
                        
998
                        foreach ($validationFields as $validationField) {
999
                            
1000
                            if (empty($ret[$validationField])) {
1001
                                $ret[$validationField] = [];
1002
                            }
1003
                            
1004
                            $ret[$validationField][] = [
1005
                                'field' => $message->getField(),
1006
                                'code' => $message->getCode(),
1007
                                'type' => $message->getType(),
1008
                                'message' => $message->getMessage(),
1009
                                'metaData' => $message->getMetaData(),
1010
                            ];
1011
                        }
1012
                    }
1013
                }
1014
            }
1015
        }
1016
        
1017
        return $ret ? : false;
1018
    }
1019
}
1020