Passed
Push — v5 ( e348bf...8a8f01 )
by Alexey
06:35
created

Model   F

Complexity

Total Complexity 449

Size/Duplication

Total Lines 1877
Duplicated Lines 0 %

Test Coverage

Coverage 9.83%

Importance

Changes 0
Metric Value
dl 0
loc 1877
rs 0.6314
c 0
b 0
f 0
ccs 98
cts 997
cp 0.0983
wmc 449

58 Methods

Rating   Name   Duplication   Size   Complexity  
D logChanges() 0 91 38
A objectName() 0 2 1
F saveModuleStorage() 0 50 13
A __isset() 0 3 1
A __construct() 0 6 2
A addRelation() 0 14 3
A afterSave() 0 1 1
A indexes() 0 2 1
A __get() 0 8 3
A beforeSave() 0 1 1
F loadRelation() 0 82 41
A afterDelete() 0 1 1
B changeCategoryTree() 0 24 2
A changeItemTree() 0 8 2
F resloveTypeValue() 0 162 46
A colPrefix() 0 4 1
C fixPrefix() 0 56 25
A deleteList() 0 6 2
A name() 0 2 2
A checkFormAccess() 0 5 3
A getRelation() 0 3 2
B __call() 0 11 5
F deleteFromModuleStorage() 0 43 11
B extract() 0 16 5
C genColParams() 0 56 22
A checkAccess() 0 5 2
A __toString() 0 2 1
A validator() 0 6 2
A genViewLink() 0 7 1
F save() 0 50 19
A createCol() 0 12 3
A pk() 0 2 1
A delete() 0 16 4
C getColValue() 0 22 9
A setParams() 0 3 2
A getColInfo() 0 2 1
B update() 0 21 5
A index() 0 2 1
A validators() 0 2 1
B getList() 0 17 6
F getCount() 0 105 32
A beforeDelete() 0 1 1
B cols() 0 12 5
A value() 0 9 3
C parseColRecursion() 0 53 14
F get_list() 0 112 32
C createTable() 0 53 13
A relations() 0 2 1
A getCatalogTree() 0 11 4
A findRelation() 0 8 3
F get() 0 69 19
A new() 0 2 1
A nameCol() 0 2 1
A __callStatic() 0 5 2
A managerFilters() 0 2 1
C checkForJoin() 0 33 8
A table() 0 2 1
C __set() 0 29 16

How to fix   Complexity   

Complex Class

Complex classes like Model often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Model, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Model
4
 *
5
 * @author Alexey Krupskiy <[email protected]>
6
 * @link http://inji.ru/
7
 * @copyright 2015 Alexey Krupskiy
8
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
9
 */
10
11
namespace Inji;
12
13
use Inji\Model\Builder;
14
15
/**
16
 * Class Model
17
 * @package Inji
18
 *
19
 * @method static Model\Builder connection($connectionName)
20
 */
21
class Model {
22
23
    public static $loaded = [];
24
25
    /**
26
     * Object storage type
27
     *
28
     * @var array
29
     */
30
    public static $storage = ['type' => 'db'];
31
32
    /**
33
     * Object name
34
     *
35
     * @var string
36
     */
37
    public static $objectName = '';
38
39
    /**
40
     * App type for separate data storage
41
     *
42
     * @var string
43
     */
44
    public $appType = 'app';
45
46
    /**
47
     * Object current params
48
     *
49
     * @var array
50
     */
51
    public $_params = [];
52
53
    /**
54
     * List of changed params in current instance
55
     *
56
     * @var array
57
     */
58
    public $_changedParams = [];
59
60
    /**
61
     * Loaded relations
62
     *
63
     * @var array
64
     */
65
    public $loadedRelations = [];
66
67
    /**
68
     * Model name where this model uses as category
69
     *
70
     * @var string
71
     */
72
    public static $treeCategory = '';
73
74
    /**
75
     * Model name who uses as category in this model
76
     *
77
     * @var string
78
     */
79
    public static $categoryModel = '';
80
81
    /**
82
     * Col labels
83
     *
84
     * @var array
85
     */
86
    public static $labels = [];
87
88
    /**
89
     * Model forms
90
     *
91
     * @var array
92
     */
93
    public static $forms = [];
94
95
    /**
96
     * Model cols
97
     *
98
     * @var array
99
     */
100
    public static $cols = [];
101
102
    /**
103
     * Options group for display inforamtion from model
104
     *
105
     * @var array
106
     */
107
    public static $view = [];
108
109
    /**
110
     * List of relations need loaded with item
111
     *
112
     * @var array
113
     */
114
    public static $needJoin = [];
115
116
    /**
117
     * List of joins who need to laod
118
     *
119
     * @var array
120
     */
121
    public static $relJoins = [];
122
123
    public $connectionName = 'default';
124
    public $dbOptions = [];
125
    public $app;
126
127
    /**
128
     * Set params when model create
129
     *
130
     * @param array $params
131
     * @param App $app
132
     */
133 3
    public function __construct($params = [], $app = null) {
134 3
        $this->setParams($params);
135 3
        if (!$app) {
136 3
            $this->app = App::$primary;
137
        } else {
138
            $this->app = $app;
139
        }
140 3
    }
141
142 1
    public static function new($params = [], $app = null) {
143 1
        return new static($params, $app);
144
    }
145
146 4
    public static function __callStatic($name, $arguments) {
147 4
        if (method_exists('Inji\Model\Builder', $name)) {
148 4
            return call_user_func_array([new Model\Builder(get_called_class()), $name], $arguments);
149
        }
150
        trigger_error('Undefined method in Inji\Model', E_USER_ERROR);
151
    }
152
153
    public static $logging = true;
154
155
    /**
156
     * return object name
157
     *
158
     * @return string
159
     */
160
    public static function objectName() {
161
        return static::$objectName;
162
    }
163
164
    /**
165
     * Retrn col value with col params and relations path
166
     *
167
     * @param Model $object
168
     * @param string $valuePath
169
     * @param boolean $convert
170
     * @param boolean $manageHref
171
     * @return string
172
     */
173
    public static function getColValue($object, $valuePath, $convert = false, $manageHref = false) {
174
        if (is_array($object)) {
175
            $object = array_shift($object);
176
        }
177
        if (strpos($valuePath, ':')) {
178
            $rel = substr($valuePath, 0, strpos($valuePath, ':'));
179
            $param = substr($valuePath, strpos($valuePath, ':') + 1);
180
            if (!$object->$rel) {
181
                $modelName = get_class($object);
182
                $relations = $modelName::relations();
183
                if (empty($relations[$rel]['type']) || $relations[$rel]['type'] == 'one') {
184
                    return $object->{$relations[$rel]['col']};
185
                }
186
                return 0;
187
            }
188
            if (strpos($valuePath, ':')) {
189
                return self::getColValue($object->$rel, $param, $convert, $manageHref);
190
            } else {
191
                return $convert ? Model::resloveTypeValue($object->$rel, $param, $manageHref) : $object->$rel->$param;
192
            }
193
        } else {
194
            return $convert ? Model::resloveTypeValue($object, $valuePath, $manageHref) : $object->$valuePath;
195
        }
196
    }
197
198
    /**
199
     * Retrun value for view
200
     *
201
     * @param Model $item
202
     * @param string $colName
203
     * @param boolean $manageHref
204
     * @param array $params
205
     * @return string
206
     */
207
    public static function resloveTypeValue($item, $colName, $manageHref = false, $colInfo = []) {
208
        $modelName = get_class($item);
209
        if (!$colInfo) {
210
            $colInfo = $modelName::getColInfo($colName);
211
        }
212
        $type = !empty($colInfo['colParams']['type']) ? $colInfo['colParams']['type'] : 'string';
213
        $value = '';
214
        switch ($type) {
215
            case 'autocomplete':
216
                $options = $colInfo['colParams']['options'];
217
                if (isset($options['snippet']) && is_string($options['snippet'])) {
218
                    $snippets = \App::$cur->Ui->getSnippets('autocomplete');
219
                    if (isset($snippets[$options['snippet']])) {
220
                        $value = $snippets[$options['snippet']]['getValueText']($item->$colName, $options['snippetParams']);
221
                    }
222
                }
223
                break;
224
            case 'select':
225
                switch ($colInfo['colParams']['source']) {
226
                    case 'model':
227
                        $sourceValue = '';
228
                        if ($item->$colName) {
229
                            $sourceValue = $colInfo['colParams']['model']::get($item->$colName);
230
                        }
231
                        $value = $sourceValue ? $sourceValue->name() : 'Не задано';
232
                        break;
233
                    case 'array':
234
                        $value = !empty($colInfo['colParams']['sourceArray'][$item->$colName]) ? $colInfo['colParams']['sourceArray'][$item->$colName] : 'Не задано';
235
                        if (is_array($value) && $value['text']) {
236
                            $value = $value['text'];
237
                        }
238
                        break;
239
                    case 'bool':
240
                        return $item->$colName ? 'Да' : 'Нет';
241
                    case 'method':
242
                        if (!empty($colInfo['colParams']['params'])) {
243
                            $values = call_user_func_array([App::$cur->{$colInfo['colParams']['module']}, $colInfo['colParams']['method']],
244
                                $colInfo['colParams']['params'] + [$item]
245
                            );
246
                        } else {
247
                            $values = \App::$cur->{$colInfo['colParams']['module']}->{$colInfo['colParams']['method']}($item);
248
                        }
249
                        $value = !empty($values[$item->$colName]) ? $values[$item->$colName] : 'Не задано';
250
                        break;
251
                    case 'void':
252
                        if (!empty($modelName::$cols[$colName]['value']['type']) && $modelName::$cols[$colName]['value']['type'] == 'moduleMethod') {
0 ignored issues
show
Bug introduced by
The property cols does not exist on string.
Loading history...
253
                            return \App::$cur->{$modelName::$cols[$colName]['value']['module']}->{$modelName::$cols[$colName]['value']['method']}($item, $colName, $modelName::$cols[$colName]);
254
                        }
255
                        break;
256
                    case 'relation':
257
                        if (strpos($colInfo['colParams']['relation'], ':')) {
258
                            $relationPath = explode(':', $colInfo['colParams']['relation']);
259
                            $relationName = array_pop($relationPath);
260
                            $curItem = $item;
261
                            foreach ($relationPath as $path) {
262
                                $curItem = $curItem->$path;
263
                            }
264
                            $itemModel = get_class($curItem);
265
                            $relation = $itemModel::getRelation($relationName);
266
                            $relModel = $relation['model'];
267
                        } else {
268
                            $itemModel = get_class($item);
269
                            $relation = $itemModel::getRelation($colInfo['colParams']['relation']);
270
                            $relModel = $relation['model'];
271
                        }
272
                        $relValue = $relModel::get($item->$colName);
273
                        $relModel = strpos($relModel, '\\') === 0 ? substr($relModel, 1) : $relModel;
274
                        if ($manageHref) {
275
                            $value = $relValue ? "<a href='/admin/" . str_replace('\\', '/view/', $relModel) . "/" . $relValue->pk() . "'>" . $relValue->name() . "</a>" : 'Не задано';
276
                        } else {
277
                            $value = $relValue ? $relValue->name() : 'Не задано';
278
                        }
279
                        break;
280
                }
281
                break;
282
            case 'image':
283
                $file = Files\File::get($item->$colName);
0 ignored issues
show
Bug introduced by
The type Inji\Files\File was not found. Did you mean Files\File? If so, make sure to prefix the type with \.
Loading history...
284
                if ($file) {
285
                    $photoId = Tools::randomString();
286
                    $value = '<a href = "' . $file->path . '" id="' . $photoId . '" rel="fgall[allimg]"><img src="' . $file->path . '?resize=60x120" /></a>';
287
                    $value .= '<script>inji.onLoad(function(){$("[rel]").fancybox();});</script>';
288
                } else {
289
                    $value = '<img src="/static/system/images/no-image.png?resize=60x120" />';
290
                }
291
                break;
292
            case 'file':
293
                $file = Files\File::get($item->$colName);
294
                if ($file) {
295
                    $value = '<a href="' . $file->path . '">' . $file->name . '.' . $file->type->ext . '</a>';
296
                } else {
297
                    $value = 'Файл не загружен';
298
                }
299
                break;
300
            case 'bool':
301
                $value = $item->$colName ? 'Да' : 'Нет';
302
                break;
303
            case 'void':
304
                if (!empty($colInfo['colParams']['value']['type']) && $colInfo['colParams']['value']['type'] == 'moduleMethod') {
305
                    return \App::$cur->{$colInfo['colParams']['value']['module']}->{$colInfo['colParams']['value']['method']}($item, $colName, $colInfo['colParams']);
306
                }
307
                break;
308
            case 'map':
309
                if ($item->$colName && json_decode($item->$colName, true)) {
310
                    $addres = json_decode($item->$colName, true);
311
                    $name = $addres['address'] ? $addres['address'] : 'lat:' . $addres['lat'] . ': lng:' . $addres['lng'];
312
                    \App::$cur->libs->loadLib('yandexMap');
313
                    ob_start();
314
                    $uid = Tools::randomString();
315
                    ?>
316
                    <div id='map<?= $uid; ?>_container' style="display:none;">
317
                        <script>/*
318
                             <div id='map<?= $uid; ?>' style="width: 100%; height: 500px"></div>
319
                             <script>
320
                             var myMap<?= $uid; ?>;
321
                             var myMap<?= $uid; ?>CurPin;
322
                             inji.onLoad(function () {
323
                             ymaps.ready(init<?= $uid; ?>);
324
                             function init<?= $uid; ?>() {
325
                             var myPlacemark;
326
                             myMap<?= $uid; ?> = new ymaps.Map("map<?= $uid; ?>", {
327
                             center: ["<?= $addres['lat'] ?>", "<?= $addres['lng']; ?>"],
328
                             zoom: 13
329
                             });
330
                             myCoords = ["<?= $addres['lat'] ?>", "<?= $addres['lng']; ?>"];
331
                             myMap<?= $uid; ?>CurPin = new ymaps.Placemark(myCoords,
332
                             {iconContent: "<?= $addres['address']; ?>"},
333
                             {preset: 'islands#greenStretchyIcon'}
334
                             );
335
                             myMap<?= $uid; ?>.geoObjects.add(myMap<?= $uid; ?>CurPin, 0);
336
                             }
337
                             window['init<?= $uid; ?>'] = init<?= $uid; ?>;
338
                             });
339
                             */</script>
340
                    </div>
341
                    <?php
342
                    $content = ob_get_contents();
343
                    ob_end_clean();
344
                    $onclick = 'inji.Ui.modals.show("' . addcslashes($addres['address'], '"') . '", $("#map' . $uid . '_container script").html().replace(/^\/\*/g, "").replace(/\*\/$/g, "")+"</script>","mapmodal' . $uid . '","modal-lg");';
345
                    $onclick .= 'return false;';
346
                    $value = "<a href ='#' onclick='{$onclick}' >{$name}</a>";
347
                    $value .= $content;
348
                } else {
349
                    $value = 'Местоположение не заданно';
350
                }
351
352
                break;
353
            case 'dynamicType':
354
                switch ($colInfo['colParams']['typeSource']) {
355
                    case 'selfMethod':
356
                        $type = $item->{$colInfo['colParams']['selfMethod']}();
357
                        if (is_array($type)) {
358
                            $value = static::resloveTypeValue($item, $colName, $manageHref, ['colParams' => $type]);
359
                        } else {
360
                            $value = static::resloveTypeValue($item, $colName, $manageHref, ['colParams' => ['type' => $type]]);
361
                        }
362
                        break;
363
                }
364
                break;
365
            default:
366
                $value = $item->$colName;
367
        }
368
        return $value;
369
    }
370
371
    /**
372
     * Fix col prefix
373
     *
374
     * @param mixed $array
375
     * @param string $searchtype
376
     * @param string $rootModel
377
     * @return null
378
     */
379
    public static function fixPrefix(&$array, $searchtype = 'key', $rootModel = '') {
380
        if (!$rootModel) {
381
            $rootModel = get_called_class();
382
        }
383
        $cols = static::cols();
384
        if (!$array) {
385
            return;
386
        }
387
        if (!is_array($array)) {
388
            if (!isset($cols[static::colPrefix() . $array]) && isset(static::$cols[$array])) {
389
                static::createCol($array);
390
                $cols = static::cols();
391
            }
392
            if (!isset($cols[$array]) && isset($cols[static::colPrefix() . $array])) {
393
                $array = static::colPrefix() . $array;
394
            } else {
395
                static::checkForJoin($array, $rootModel);
396
            }
397
            return;
398
        }
399
        switch ($searchtype) {
400
            case 'key':
401
                foreach ($array as $key => $item) {
402
                    if (!isset($cols[static::colPrefix() . $key]) && isset(static::$cols[$key])) {
403
                        static::createCol($key);
404
                        $cols = static::cols(true);
405
                    }
406
                    if (!isset($cols[$key]) && isset($cols[static::colPrefix() . $key])) {
407
                        $array[static::colPrefix() . $key] = $item;
408
                        unset($array[$key]);
409
                        $key = static::colPrefix() . $key;
410
                    }
411
                    if (is_array($array[$key])) {
412
                        static::fixPrefix($array[$key], 'key', $rootModel);
413
                    } else {
414
                        static::checkForJoin($key, $rootModel);
415
                    }
416
                }
417
                break;
418
            case 'first':
419
                if (isset($array[0]) && is_string($array[0])) {
420
                    if (!isset($cols[static::colPrefix() . $array[0]]) && isset(static::$cols[$array[0]])) {
421
                        static::createCol($array[0]);
422
                        $cols = static::cols();
423
                    }
424
                    if (!isset($cols[$array[0]]) && isset($cols[static::colPrefix() . $array[0]])) {
425
                        $array[0] = static::colPrefix() . $array[0];
426
                    } else {
427
                        static::checkForJoin($array[0], $rootModel);
428
                    }
429
                } elseif (isset($array[0]) && is_array($array[0])) {
430
                    foreach ($array as &$item) {
431
                        static::fixPrefix($item, 'first', $rootModel);
432
                    }
433
                }
434
                break;
435
        }
436
    }
437
438
    /**
439
     * @param boolean $new
440
     */
441 3
    public function logChanges($new) {
442 3
        if (!App::$cur->db->connect || !App::$cur->dashboard) {
0 ignored issues
show
Bug Best Practice introduced by
The property dashboard does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
443 3
            return false;
444
        }
445
        $class = get_class($this);
446
        if (!Model::$logging || !$class::$logging || (is_string($class::$logging) && $class::$logging != ($new ? 'new' : 'changes'))) {
0 ignored issues
show
Bug introduced by
The property logging does not exist on string.
Loading history...
447
            return false;
448
        }
449
        $user_id = class_exists('Users\User') ? \Users\User::$cur->id : 0;
450
        if (!$new && !empty($this->_changedParams)) {
451
            $activity = new Dashboard\Activity([
0 ignored issues
show
Bug introduced by
The type Inji\Dashboard\Activity was not found. Did you mean Dashboard\Activity? If so, make sure to prefix the type with \.
Loading history...
452
                'user_id' => $user_id,
453
                'module' => substr($class, 0, strpos($class, '\\')),
454
                'model' => $class,
455
                'item_id' => $this->pk(),
456
                'type' => 'changes'
457
            ]);
458
            $changes_text = [];
459
            foreach ($this->_changedParams as $fullColName => $oldValue) {
460
                $colName = substr($fullColName, strlen($class::colPrefix()));
461
                if (isset($class::$cols[$colName]['logging']) && $class::$cols[$colName]['logging'] === false) {
0 ignored issues
show
Bug introduced by
The property cols does not exist on string.
Loading history...
462
                    continue;
463
                }
464
                if (!isset($class::$cols[$colName]['logging']) || $class::$cols[$colName]['logging'] !== 'noValue') {
465
                    $oldValueText = $oldValue;
466
                    if (isset($class::$cols[$colName]) && $class::$cols[$colName]['type'] === 'select') {
467
                        switch ($class::$cols[$colName]['source']) {
468
                            case 'array':
469
                                $oldValueText = isset($class::$cols[$colName]['sourceArray'][$oldValue]) ? $class::$cols[$colName]['sourceArray'][$oldValue] : $oldValue;
470
                                break;
471
                            case 'relation':
472
                                $relation = $class::getRelation($class::$cols[$colName]['relation']);
473
                                $relModel = $relation['model'];
474
                                $rel = $relModel::get($oldValue);
475
                                if ($rel) {
476
                                    $oldValueText = $rel->name();
477
                                }
478
                        }
479
                    }
480
                    $newValueText = $this->$colName;
481
                    if (isset($class::$cols[$colName]) && $class::$cols[$colName]['type'] === 'select') {
482
                        switch ($class::$cols[$colName]['source']) {
483
                            case 'array':
484
                                $newValueText = isset($class::$cols[$colName]['sourceArray'][$this->$colName]) ? $class::$cols[$colName]['sourceArray'][$this->$colName] : $this->$colName;
485
                                break;
486
                            case 'relation':
487
                                $relation = $class::getRelation($class::$cols[$colName]['relation']);
488
                                $relModel = $relation['model'];
489
                                $rel = $relModel::get($this->$colName);
490
                                if ($rel) {
491
                                    $newValueText = $rel->name();
492
                                }
493
                        }
494
                    }
495
                }
496
                if ((!isset($class::$cols[$colName]['logging']) || $class::$cols[$colName]['logging'] !== 'noValue') && strlen($oldValueText) + strlen($newValueText) < 200) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $oldValueText does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $newValueText does not seem to be defined for all execution paths leading up to this point.
Loading history...
497
                    $changes_text[] = (!empty($class::$labels[$colName]) ? $class::$labels[$colName] : $colName) . ": \"{$oldValueText}\" => \"{$newValueText}\"";
0 ignored issues
show
Bug introduced by
The property labels does not exist on string.
Loading history...
498
                } else {
499
                    $changes_text[] = !empty($class::$labels[$colName]) ? $class::$labels[$colName] : $colName;
500
                }
501
            }
502
            if (!$changes_text) {
503
                return false;
504
            }
505
            $activity->changes_text = implode(', ', $changes_text);
506
            $activity->save();
507
            foreach ($this->_changedParams as $fullColName => $oldValue) {
508
                $colName = substr($fullColName, strlen($class::colPrefix()));
509
                if (isset($class::$cols[$colName]['logging']) && !$class::$cols[$colName]['logging']) {
510
                    continue;
511
                }
512
                $colName = substr($fullColName, strlen($class::colPrefix()));
513
                $change = new Dashboard\Activity\Change([
0 ignored issues
show
Bug introduced by
The type Inji\Dashboard\Activity\Change was not found. Did you mean Dashboard\Activity\Change? If so, make sure to prefix the type with \.
Loading history...
514
                    'activity_id' => $activity->id,
515
                    'col' => $colName,
516
                    'old' => $oldValue,
517
                    'new' => $this->$colName
518
                ]);
519
                $change->save();
520
            }
521
        } elseif ($new) {
522
            $activity = new Dashboard\Activity([
523
                'user_id' => $user_id,
524
                'module' => substr($class, 0, strpos($class, '\\')),
525
                'model' => $class,
526
                'item_id' => $this->pk(),
527
                'type' => 'new'
528
            ]);
529
            $activity->save();
530
        }
531
        return true;
532
    }
533
534
    /**
535
     * Check model relations path and load need relations
536
     *
537
     * @param string $col
538
     * @param string $rootModel
539
     */
540
    public static function checkForJoin(&$col, $rootModel) {
541
542
        if (strpos($col, ':') !== false) {
543
            $relations = static::relations();
544
            if (isset($relations[substr($col, 0, strpos($col, ':'))])) {
545
                $rel = substr($col, 0, strpos($col, ':'));
546
                $col = substr($col, strpos($col, ':') + 1);
547
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
548
                $joinName = $relations[$rel]['model'] . '_' . $rel;
549
                switch ($type) {
550
                    case 'to':
551
                        $relCol = $relations[$rel]['col'];
552
                        static::fixPrefix($relCol);
553
                        $rootModel::$relJoins[$joinName] = [$relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol, 'left', ''];
0 ignored issues
show
Bug introduced by
The property relJoins does not exist on string.
Loading history...
554
                        break;
555
                    case 'one':
556
                    case 'many':
557
                        $relCol = $relations[$rel]['col'];
558
                        $relations[$rel]['model']::fixPrefix($relCol);
559
                        $rootModel::$relJoins[$joinName] = [$relations[$rel]['model']::table(), static::index() . ' = ' . $relCol, 'left', ''];
560
                        break;
561
                    case 'relModel':
562
                        $relation = $relations[$rel];
563
                        $fixedCol = $relation['model']::index();
564
                        $relation['relModel']::fixPrefix($fixedCol);
565
                        $joinName = $relations[$rel]['relModel'] . '_' . $rel;
566
                        $rootModel::$relJoins[$joinName] = [$relation['relModel']::table(), $relation['relModel']::colPrefix() . static::index() . ' = ' . static::index(), 'INNER'];
567
                        $joinName = $relations[$rel]['model'] . '_' . $rel;
568
                        $rootModel::$relJoins[$joinName] = [$relation['model']::table(), $relation['relModel']::colPrefix() . $relation['model']::index() . ' = ' . $relation['model']::index(), 'INNER'];
569
                        //$rootModel::$relJoins[$joinName] = [$relations[$rel]['model']::table(), static::index() . ' = ' . $relCol, 'left', ''];
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
570
                        break;
571
                }
572
                $relations[$rel]['model']::fixPrefix($col, 'key', $rootModel);
573
            }
574
        }
575
    }
576
577
    /**
578
     * Return full col information
579
     *
580
     * @param string $col
581
     * @return array
582
     */
583
    public static function getColInfo($col) {
584
        return static::parseColRecursion($col);
585
    }
586
587
    /**
588
     * Information extractor for col relations path
589
     *
590
     * @param string $info
591
     * @return array
592
     */
593
    public static function parseColRecursion($info) {
594
        if (is_string($info)) {
595
            $info = ['col' => $info, 'rawCol' => $info, 'rawModel' => get_called_class(), 'modelName' => get_called_class(), 'label' => $info, 'joins' => []];
596
        }
597
        if ($info['col'] === 'id') {
598
            $info['colParams'] = [
599
                'type' => 'number',
600
            ];
601
            return $info;
602
        }
603
        if (strpos($info['col'], ':') !== false) {
604
            $relations = static::relations();
605
            if (isset($relations[substr($info['col'], 0, strpos($info['col'], ':'))])) {
606
                $rel = substr($info['col'], 0, strpos($info['col'], ':'));
607
                $info['col'] = substr($info['col'], strpos($info['col'], ':') + 1);
608
                //$info['modelName'] = $relations[$rel]['model'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
609
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
610
                $joinName = $relations[$rel]['model'] . '_' . $rel;
611
                switch ($type) {
612
                    case 'to':
613
                        $relCol = $relations[$rel]['col'];
614
                        static::fixPrefix($relCol);
615
                        $info['joins'][$joinName] = [$relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol];
616
                        break;
617
                    case 'one':
618
                        $relCol = $relations[$rel]['col'];
619
                        $relations[$rel]['model']::fixPrefix($relCol);
620
                        $info['joins'][$joinName] = [$relations[$rel]['model']::table(), static::index() . ' = ' . $relCol];
621
                        break;
622
                }
623
                $info = $relations[$rel]['model']::parseColRecursion($info);
624
            }
625
        } else {
626
            $cols = static::cols();
627
            if (!empty(static::$labels[$info['col']])) {
628
                $info['label'] = static::$labels[$info['col']];
629
            }
630
631
            if (isset(static::$cols[$info['col']])) {
632
                $info['colParams'] = static::$cols[$info['col']];
633
            } elseif (isset(static::$cols[str_replace(static::colPrefix(), '', $info['col'])])) {
634
                $info['colParams'] = static::$cols[str_replace(static::colPrefix(), '', $info['col'])];
635
            } else {
636
                $info['colParams'] = [];
637
            }
638
            if (!isset($cols[$info['col']]) && isset($cols[static::colPrefix() . $info['col']])) {
639
                $info['col'] = static::colPrefix() . $info['col'];
640
            }
641
        }
642
        if (!empty(static::$labels[$info['rawCol']])) {
643
            $info['label'] = static::$labels[$info['rawCol']];
644
        }
645
        return $info;
646
    }
647
648
    /**
649
     * Return actual cols from data base
650
     *
651
     * @param boolean $refresh
652
     * @return array
653
     */
654
    public static function cols($refresh = false) {
655
        if (static::$storage['type'] == 'moduleConfig') {
656
            return [];
657
        }
658
        if (empty(\Inji\Model::$cols[static::table()]) || $refresh) {
659
            \Inji\Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method getTableCols() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

659
            /** @scrutinizer ignore-call */ 
660
            \Inji\Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
Loading history...
660
        }
661
        if (!isset(\Inji\Model::$cols[static::table()])) {
662
            static::createTable();
663
            \Inji\Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
664
        }
665
        return \Inji\Model::$cols[static::table()];
666
    }
667
668
    /**
669
     * Return cols indexes for create tables
670
     *
671
     * @return array
672
     */
673
    public static function indexes() {
674
        return [];
675
    }
676
677
    /**
678
     * Generate params string for col by name
679
     *
680
     * @param string $colName
681
     * @return false|string
682
     */
683
    public static function genColParams($colName) {
684
        if (empty(static::$cols[$colName]) || static::$storage['type'] == 'moduleConfig') {
685
            return false;
686
        }
687
        $null = ' NULL';
688
        if (empty(static::$cols[$colName]['null'])) {
689
            $null = ' NOT NULL';
690
        }
691
692
        $params = false;
693
        switch (static::$cols[$colName]['type']) {
694
            case 'select':
695
                switch (static::$cols[$colName]['source']) {
696
                    case 'relation':
697
                        $params = 'int(11) UNSIGNED' . $null;
698
                        break;
699
                    default:
700
                        $params = 'varchar(255)' . $null;
701
                }
702
                break;
703
            case 'image':
704
            case 'file':
705
                $params = 'int(11) UNSIGNED' . $null;
706
                break;
707
            case 'number':
708
                $params = 'int(11)' . $null;
709
                break;
710
            case 'text':
711
            case 'email':
712
                $params = 'varchar(255)' . $null;
713
                break;
714
            case 'html':
715
            case 'textarea':
716
            case 'json':
717
            case 'password':
718
            case 'dynamicType':
719
            case 'map':
720
                $params = 'text' . $null;
721
                break;
722
            case 'bool':
723
                $params = 'tinyint(1) UNSIGNED' . $null;
724
                break;
725
            case 'decimal':
726
                $params = 'decimal(8, 2)' . $null;
727
                break;
728
            case 'time':
729
                $params = 'time' . $null;
730
                break;
731
            case 'date':
732
                $params = 'date' . $null;
733
                break;
734
            case 'dateTime':
735
                $params = 'timestamp' . $null;
736
                break;
737
        }
738
        return $params;
739
    }
740
741
    /**
742
     * Create new col in data base
743
     *
744
     * @param string $colName
745
     * @return boolean|integer
746
     */
747
    public static function createCol($colName) {
748
        $cols = static::cols();
749
        if (!empty($cols[static::colPrefix() . $colName])) {
750
            return true;
751
        }
752
        $params = static::genColParams($colName);
753
        if ($params === false) {
754
            return false;
755
        }
756
        $result = App::$cur->db->addCol(static::table(), static::colPrefix() . $colName, $params);
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method addCol() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

756
        /** @scrutinizer ignore-call */ 
757
        $result = App::$cur->db->addCol(static::table(), static::colPrefix() . $colName, $params);
Loading history...
757
        static::cols(true);
758
        return $result;
759
    }
760
761
    public static function createTable() {
762
        if (static::$storage['type'] == 'moduleConfig') {
763
            return true;
764
        }
765
        if (!App::$cur->db) {
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
766
            return false;
767
        }
768
769
        $query = App::$cur->db->newQuery();
0 ignored issues
show
Bug introduced by
The method newQuery() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

769
        /** @scrutinizer ignore-call */ 
770
        $query = App::$cur->db->newQuery();
Loading history...
770
        if (!$query) {
771
            return false;
772
        }
773
774
        if (!isset($this)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $this seems to never exist and therefore isset should always be false.
Loading history...
775
            $tableName = static::table();
776
            $colPrefix = static::colPrefix();
777
            $indexes = static::indexes();
778
        } else {
779
            $tableName = $this->table();
780
            $colPrefix = $this->colPrefix();
781
            $indexes = $this->indexes();
782
        }
783
        if (App::$cur->db->tableExist($tableName)) {
0 ignored issues
show
Bug introduced by
The method tableExist() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

783
        if (App::$cur->db->/** @scrutinizer ignore-call */ tableExist($tableName)) {
Loading history...
784
            return true;
785
        }
786
        $cols = [
787
            $colPrefix . 'id' => 'pk'
788
        ];
789
        $className = get_called_class();
790
        if (!empty($className::$cols)) {
0 ignored issues
show
Bug introduced by
The property cols does not exist on string.
Loading history...
791
            foreach ($className::$cols as $colName => $colParams) {
792
                if ($colName == 'date_create') {
793
                    $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
794
                    continue;
795
                }
796
                $params = $className::genColParams($colName);
797
                if ($params) {
798
                    $cols[$colPrefix . $colName] = $params;
799
                }
800
            }
801
        }
802
        if (empty($cols[$colPrefix . 'date_create'])) {
803
            $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
804
        }
805
        $tableIndexes = [];
806
        if ($indexes) {
807
            foreach ($indexes as $indexName => $index) {
808
                $tableIndexes[] = $index['type'] . ' ' . App::$cur->db->table_prefix . $indexName . ' (' . implode(',', $index['cols']) . ')';
0 ignored issues
show
Bug introduced by
The property table_prefix does not seem to exist on Inji\Module.
Loading history...
809
            }
810
        }
811
812
        $query->createTable($tableName, $cols, $tableIndexes);
813
        return true;
814
    }
815
816
    /**
817
     * Return table name
818
     *
819
     * @return string
820
     */
821 4
    public static function table() {
822 4
        return strtolower(str_replace('\\', '_', get_called_class()));
823
    }
824
825
    /**
826
     * Return table index col name
827
     *
828
     * @return string
829
     */
830
    public static function index() {
831
        return static::colPrefix() . 'id';
832
    }
833
834
    /**
835
     * Return col prefix
836
     *
837
     * @return string
838
     */
839
    public static function colPrefix() {
840
        $classPath = explode('\\', get_called_class());
841
        $classPath = array_slice($classPath, 2);
842
        return strtolower(implode('_', $classPath)) . '_';
843
    }
844
845
    /**
846
     * return relations list
847
     *
848
     * @return array
849
     */
850 1
    public static function relations() {
851 1
        return [];
852
    }
853
854
    /**
855
     * views list
856
     *
857
     * @return array
858
     */
859
    public static $views = [];
860
861
    /**
862
     * Return name of col with object name
863
     *
864
     * @return string
865
     */
866
    public static function nameCol() {
867
        return 'name';
868
    }
869
870
    /**
871
     * Return object name
872
     *
873
     * @return string
874
     */
875
    public function name() {
876
        return $this->{$this->nameCol()} ? $this->{$this->nameCol()} : '№' . $this->pk();
877
    }
878
879
    /**
880
     * Get single object from data base
881
     *
882
     * @param mixed $param
883
     * @param string $col
884
     * @param array $options
885
     * @return boolean|static
886
     */
887
    public static function get($param, $col = null, $options = []) {
888
        if (static::$storage['type'] == 'moduleConfig') {
889
            return static::getFromModuleStorage($param, $col, $options);
0 ignored issues
show
Bug introduced by
The method getFromModuleStorage() does not exist on Inji\Model. Since you implemented __callStatic, 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

889
            return static::/** @scrutinizer ignore-call */ getFromModuleStorage($param, $col, $options);
Loading history...
890
        }
891
        if (!empty($col)) {
892
            static::fixPrefix($col);
893
        }
894
895
        if (is_array($param)) {
896
            static::fixPrefix($param, 'first');
897
        }
898
        $query = App::$cur->db->newQuery();
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
899
        foreach (static::$relJoins as $join) {
900
            $query->join($join);
901
        }
902
        static::$relJoins = [];
903
        foreach (static::$needJoin as $rel) {
904
            $relations = static::relations();
905
            if (isset($relations[$rel])) {
906
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
907
                switch ($type) {
908
                    case 'to':
909
                        $relCol = $relations[$rel]['col'];
910
                        static::fixPrefix($relCol);
911
                        $query->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
912
                        break;
913
                    case 'one':
914
                        $col = $relations[$rel]['col'];
915
                        $relations[$rel]['model']::fixPrefix($col);
916
                        $query->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
917
                        break;
918
                }
919
            }
920
        }
921
        static::$needJoin = [];
922
        if (is_array($param)) {
923
            $query->where($param);
924
        } else {
925
            if ($col === null) {
926
927
                $col = static::index();
928
            }
929
            if ($param !== null) {
930
                $cols = static::cols();
931
                if (!isset($cols[$col]) && isset($cols[static::colPrefix() . $col])) {
932
                    $col = static::colPrefix() . $col;
933
                }
934
                $query->where($col, $param);
935
            } else {
936
                return false;
937
            }
938
        }
939
        if (!$query->where) {
940
            return false;
941
        }
942
        try {
943
            $result = $query->select(static::table());
944
        } catch (\PDOException $exc) {
945
            if ($exc->getCode() == '42S02') {
946
                static::createTable();
947
            } else {
948
                throw $exc;
949
            }
950
            $result = $query->select(static::table());
951
        }
952
        if (!$result) {
953
            return false;
954
        }
955
        return $result->fetch(get_called_class());
956
    }
957
958
    /**
959
     * Old method
960
     *
961
     * @param type $options
0 ignored issues
show
Bug introduced by
The type Inji\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
962
     * @return Array
963
     */
964
    public static function get_list($options = [], $debug = false) {
965
        $query = App::$cur->db->newQuery();
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
966
        if (!$query) {
967
            return [];
968
        }
969
        if (!empty($options['where'])) {
970
            $query->where($options['where']);
971
        }
972
        if (!empty($options['cols'])) {
973
            $query->cols = $options['cols'];
974
        }
975
        if (!empty($options['group'])) {
976
            $query->group($options['group']);
977
        }
978
        if (!empty($options['having'])) {
979
            $query->having($options['having']);
980
        }
981
        if (!empty($options['order'])) {
982
            $query->order($options['order']);
983
        }
984
        if (!empty($options['join'])) {
985
            $query->join($options['join']);
986
        }
987
        if (!empty($options['distinct'])) {
988
            $query->distinct = $options['distinct'];
989
        }
990
991
        foreach (static::$needJoin as $rel) {
992
            $relations = static::relations();
993
            foreach ($query->join as $item) {
994
                if ($item[0] === $relations[$rel]['model']::table() && $item[3] === '') {
995
                    continue 2;
996
                }
997
            }
998
            if (isset($relations[$rel])) {
999
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
1000
                switch ($type) {
1001
                    case 'to':
1002
                        $relCol = $relations[$rel]['col'];
1003
                        static::fixPrefix($relCol);
1004
                        $query->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
1005
                        break;
1006
                    case 'one':
1007
                        $col = $relations[$rel]['col'];
1008
                        $relations[$rel]['model']::fixPrefix($col);
1009
                        $query->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
1010
                        break;
1011
                }
1012
            }
1013
        }
1014
        static::$needJoin = [];
1015
1016
        foreach (static::$relJoins as $join) {
1017
            foreach ($query->join as $item) {
1018
                if ($item[0] === $join[0] && $item[3] === $join[3]) {
1019
                    continue 2;
1020
                }
1021
            }
1022
            $query->join($join);
1023
        }
1024
        static::$relJoins = [];
1025
        if (!empty($options['limit'])) {
1026
            $limit = (int)$options['limit'];
1027
        } else {
1028
            $limit = 0;
1029
        }
1030
        if (!empty($options['start'])) {
1031
            $start = (int)$options['start'];
1032
        } else {
1033
            $start = 0;
1034
        }
1035
        if ($limit || $start) {
1036
            $query->limit($start, $limit);
1037
        }
1038
        if (isset($options['key'])) {
1039
            $key = $options['key'];
1040
        } else {
1041
            $key = static::index();
1042
        }
1043
1044
        if ($debug) {
1045
            $query->operation = 'SELECT';
1046
            $query->table = static::table();
1047
            return $query->buildQuery();
1048
        }
1049
        try {
1050
            $query->operation = 'SELECT';
1051
            $query->table = static::table();
1052
            $queryArr = $query->buildQuery();
1053
            $result = $query->query($queryArr);
1054
        } catch (PDOException $exc) {
0 ignored issues
show
Bug introduced by
The type Inji\PDOException was not found. Did you mean PDOException? If so, make sure to prefix the type with \.
Loading history...
1055
            if ($exc->getCode() == '42S02') {
1056
                static::createTable();
1057
                $result = $query->query($queryArr);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $queryArr does not seem to be defined for all execution paths leading up to this point.
Loading history...
1058
            } else {
1059
                throw $exc;
1060
            }
1061
        }
1062
1063
        if (!empty($options['array'])) {
1064
            static::fixPrefix($key);
1065
            return $result->getArray($key);
1066
        }
1067
        $list = $result->getObjects(get_called_class(), $key);
1068
        if (!empty($options['forSelect'])) {
1069
            $return = [];
1070
            foreach ($list as $key => $item) {
1071
                $return[$key] = $item->name();
1072
            }
1073
            return $return;
1074
        }
1075
        return $list;
1076
    }
1077
1078
    /**
1079
     * Return list of objects from data base
1080
     *
1081
     * @param array $options
1082
     * @return static[]
1083
     */
1084
    public static function getList($options = [], $debug = false) {
1085
        if (static::$storage['type'] != 'db') {
1086
            return static::getListFromModuleStorage($options);
0 ignored issues
show
Bug introduced by
The method getListFromModuleStorage() does not exist on Inji\Model. Since you implemented __callStatic, 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

1086
            return static::/** @scrutinizer ignore-call */ getListFromModuleStorage($options);
Loading history...
1087
        }
1088
        if (!empty($options['where'])) {
1089
            static::fixPrefix($options['where'], 'first');
1090
        }
1091
        if (!empty($options['group'])) {
1092
            static::fixPrefix($options['group'], 'first');
1093
        }
1094
        if (!empty($options['order'])) {
1095
            static::fixPrefix($options['order'], 'first');
1096
        }
1097
        if (!empty($options['having'])) {
1098
            static::fixPrefix($options['having'], 'first');
1099
        }
1100
        return static::get_list($options, $debug);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type array; however, parameter $options of Inji\Model::get_list() does only seem to accept Inji\type, 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

1100
        return static::get_list(/** @scrutinizer ignore-type */ $options, $debug);
Loading history...
1101
    }
1102
1103
1104
    /**
1105
     * Return count of records from data base
1106
     *
1107
     * @param array $options
1108
     * @return array|int
1109
     */
1110
    public static function getCount($options = []) {
1111
        if (static::$storage['type'] == 'moduleConfig') {
1112
            return static::getCountFromModuleStorage($options);
0 ignored issues
show
Bug introduced by
The method getCountFromModuleStorage() does not exist on Inji\Model. Since you implemented __callStatic, 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

1112
            return static::/** @scrutinizer ignore-call */ getCountFromModuleStorage($options);
Loading history...
1113
        }
1114
        $query = App::$cur->db->newQuery();
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
1115
        if (!$query) {
1116
            return 0;
1117
        }
1118
        if (!empty($options['where'])) {
1119
            static::fixPrefix($options['where'], 'first');
1120
        }
1121
        if (!empty($options['group'])) {
1122
            static::fixPrefix($options['group'], 'first');
1123
        }
1124
        if (!empty($options['order'])) {
1125
            static::fixPrefix($options['order'], 'first');
1126
        }
1127
        if (!empty($options['where'])) {
1128
            $query->where($options['where']);
1129
        }
1130
        if (!empty($options['join'])) {
1131
            $query->join($options['join']);
1132
        }
1133
        if (!empty($options['order'])) {
1134
            $query->order($options['order']);
1135
        }
1136
        if (!empty($options['limit'])) {
1137
            $limit = (int)$options['limit'];
1138
        } else {
1139
            $limit = 0;
1140
        }
1141
        if (!empty($options['start'])) {
1142
            $start = (int)$options['start'];
1143
        } else {
1144
            $start = 0;
1145
        }
1146
        if ($limit || $start) {
1147
            $query->limit($start, $limit);
1148
        }
1149
1150
        foreach (static::$needJoin as $rel) {
1151
            $relations = static::relations();
1152
            foreach ($query->join as $item) {
1153
                if ($item[0] === $relations[$rel]['model']::table() && $item[3] === '') {
1154
                    continue 2;
1155
                }
1156
            }
1157
            if (isset($relations[$rel])) {
1158
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
1159
                switch ($type) {
1160
                    case 'to':
1161
                        $relCol = $relations[$rel]['col'];
1162
                        static::fixPrefix($relCol);
1163
                        $query->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
1164
                        break;
1165
                    case 'one':
1166
                        $col = $relations[$rel]['col'];
1167
                        $relations[$rel]['model']::fixPrefix($col);
1168
                        $query->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
1169
                        break;
1170
                }
1171
            }
1172
        }
1173
        static::$needJoin = [];
1174
        foreach (static::$relJoins as $join) {
1175
            foreach ($query->join as $item) {
1176
                if ($item[0] === $join[0] && $item[3] === $join[3]) {
1177
                    continue 2;
1178
                }
1179
            }
1180
            $query->join($join);
1181
        }
1182
        static::$relJoins = [];
1183
        $cols = 'COUNT(';
1184
1185
        if (!empty($options['distinct'])) {
1186
            if (is_bool($options['distinct'])) {
1187
                $cols .= 'DISTINCT *';
1188
            } else {
1189
                $cols .= "DISTINCT {$options['distinct']}";
1190
            }
1191
        } else {
1192
            $cols .= '*';
1193
        }
1194
        $cols .= ') as `count`' . (!empty($options['cols']) ? ',' . $options['cols'] : '');
1195
        $query->cols = $cols;
1196
        if (!empty($options['group'])) {
1197
            $query->group($options['group']);
1198
        }
1199
        try {
1200
            $result = $query->select(static::table());
1201
        } catch (PDOException $exc) {
1202
            if ($exc->getCode() == '42S02') {
1203
                static::createTable();
1204
            } else {
1205
                throw $exc;
1206
            }
1207
            $result = $query->select(static::table());
1208
        }
1209
        if (!empty($options['group'])) {
1210
            $count = $result->getArray();
1211
            return $count;
1212
        } else {
1213
            $count = $result->fetch();
1214
            return $count['count'];
1215
        }
1216
    }
1217
1218
    /**
1219
     * Update records in data base
1220
     *
1221
     * @param array $params
1222
     * @param array $where
1223
     * @return false|null
1224
     */
1225
    public static function update($params, $where = []) {
1226
        static::fixPrefix($params);
1227
1228
        $cols = self::cols();
1229
1230
        $values = [];
1231
        foreach ($cols as $col => $param) {
1232
            if (isset($params[$col])) {
1233
                $values[$col] = $params[$col];
1234
            }
1235
        }
1236
        if (empty($values)) {
1237
            return false;
1238
        }
1239
1240
        if (!empty($where)) {
1241
            static::fixPrefix($where, 'first');
1242
1243
            App::$cur->db->where($where);
0 ignored issues
show
Bug introduced by
The method where() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

1243
            App::$cur->db->/** @scrutinizer ignore-call */ 
1244
                           where($where);
Loading history...
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
1244
        }
1245
        App::$cur->db->update(static::table(), $values);
0 ignored issues
show
Bug introduced by
The method update() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

1245
        App::$cur->db->/** @scrutinizer ignore-call */ 
1246
                       update(static::table(), $values);
Loading history...
1246
    }
1247
1248
    /**
1249
     * Return primary key of object
1250
     *
1251
     * @return mixed
1252
     */
1253 4
    public function pk() {
1254 4
        return $this->{$this->index()};
1255
    }
1256
1257
    /**
1258
     * Before save trigger
1259
     */
1260 3
    public function beforeSave() {
1261
1262 3
    }
1263
1264
    /**
1265
     * Save object to module storage
1266
     *
1267
     * @param array $options
1268
     * @return boolean
1269
     */
1270
    public function saveModuleStorage($options) {
1271
1272
        $col = static::index();
1273
        $id = $this->pk();
1274
        $appType = '';
1275
        $classPath = explode('\\', get_called_class());
1276
1277
        if (!empty(static::$storage['options']['share'])) {
1278
            $moduleConfig = Config::share($classPath[0]);
1279
        } else {
1280
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
0 ignored issues
show
Bug introduced by
strpos(static::storage['...'], 'system') !== false of type boolean is incompatible with the type Inji\App expected by parameter $app of Inji\Config::module(). ( Ignorable by Annotation )

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

1280
            $moduleConfig = Config::module($classPath[0], /** @scrutinizer ignore-type */ strpos(static::$storage['type'], 'system') !== false);
Loading history...
1281
        }
1282
1283
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1284
            if (empty($options['appType'])) {
1285
                $appType = App::$cur->type;
1286
            } else {
1287
                $appType = $options['appType'];
1288
            }
1289
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1290
        } else {
1291
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1292
        }
1293
        if (empty($storage[$classPath[1]])) {
1294
            $storage[$classPath[1]] = [];
1295
        }
1296
        if ($id) {
1297
            foreach ($storage[$classPath[1]] as $key => $item) {
1298
                if ($item[$col] == $id) {
1299
                    $storage[$classPath[1]][$key] = $this->_params;
1300
                    break;
1301
                }
1302
            }
1303
        } else {
1304
            $id = !empty($storage['scheme'][$classPath[1]]['ai']) ? $storage['scheme'][$classPath[1]]['ai'] : 1;
1305
            $this->$col = $id;
1306
            $storage['scheme'][$classPath[1]]['ai'] = $id + 1;
1307
            $storage[$classPath[1]][] = $this->_params;
1308
        }
1309
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1310
            $moduleConfig['storage'][$appType] = $storage;
1311
        } else {
1312
            $moduleConfig['storage'] = $storage;
1313
        }
1314
        if (empty(static::$storage['options']['share'])) {
1315
            Config::save('module', $moduleConfig, $classPath[0]);
1316
        } else {
1317
            Config::save('share', $moduleConfig, $classPath[0]);
1318
        }
1319
        return true;
1320
    }
1321
1322
    /**
1323
     * Update tree path category
1324
     */
1325
    public function changeCategoryTree() {
1326
        $class = get_class($this);
1327
        $itemModel = $class::$treeCategory;
0 ignored issues
show
Bug introduced by
The property treeCategory does not exist on string.
Loading history...
1328
        $oldPath = $this->tree_path;
1329
        $newPath = $this->getCatalogTree($this);
1330
        $itemsTable = \App::$cur->db->table_prefix . $itemModel::table();
1331
        $itemTreeCol = $itemModel::colPrefix() . 'tree_path';
1332
        $categoryTreeCol = $this->colPrefix() . 'tree_path';
1333
        $categoryTable = \App::$cur->db->table_prefix . $this->table();
1334
        if ($oldPath) {
1335
            \App::$cur->db->query('UPDATE
1336
                ' . $categoryTable . ' 
1337
                    SET 
1338
                        ' . $categoryTreeCol . ' = REPLACE(' . $categoryTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $newPath . $this->id . '/' . '") 
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Inji\Model. Since you implemented __get, consider adding a @property annotation.
Loading history...
1339
                    WHERE ' . $categoryTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1340
1341
            \App::$cur->db->query('UPDATE
1342
                ' . $itemsTable . '
1343
                    SET 
1344
                        ' . $itemTreeCol . ' = REPLACE(' . $itemTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $newPath . $this->id . '/' . '") 
1345
                    WHERE ' . $itemTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1346
        }
1347
        $itemModel::update([$itemTreeCol => $newPath . $this->id . '/'], [$itemModel::colPrefix() . $this->index(), $this->id]);
1348
        $this->tree_path = $newPath;
0 ignored issues
show
Bug Best Practice introduced by
The property tree_path does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1349
    }
1350
1351
    /**
1352
     * Return tree path
1353
     *
1354
     * @param \Inji\Model $catalog
1355
     * @return string
1356
     */
1357
    public function getCatalogTree($catalog) {
1358
        $catalogClass = get_class($catalog);
1359
        $catalogParent = $catalogClass::get($catalog->parent_id);
0 ignored issues
show
Bug Best Practice introduced by
The property parent_id does not exist on Inji\Model. Since you implemented __get, consider adding a @property annotation.
Loading history...
1360
        if ($catalog && $catalogParent) {
1361
            if ($catalogParent->tree_path) {
1362
                return $catalogParent->tree_path . $catalogParent->id . '/';
1363
            } else {
1364
                return $this->getCatalogTree($catalogParent) . $catalogParent->id . '/';
1365
            }
1366
        }
1367
        return '/';
1368
    }
1369
1370
    /**
1371
     * Update tree path item
1372
     */
1373
    public function changeItemTree() {
1374
        $class = get_class($this);
1375
        $categoryModel = $class::$categoryModel;
0 ignored issues
show
Bug introduced by
The property categoryModel does not exist on string.
Loading history...
1376
        $category = $categoryModel::get($this->{$categoryModel::index()});
1377
        if ($category) {
1378
            $this->tree_path = $category->tree_path . $category->pk() . '/';
0 ignored issues
show
Bug Best Practice introduced by
The property tree_path does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1379
        } else {
1380
            $this->tree_path = '/';
1381
        }
1382
    }
1383
1384
    /**
1385
     * Save object to data base
1386
     *
1387
     * @param array $options
1388
     * @return boolean|int
1389
     */
1390 3
    public function save($options = []) {
1391
1392 3
        $builder = new Builder(get_called_class(), $this->app);
1393 3
        $builder->connection($this->connectionName);
1394 3
        foreach ($this->dbOptions as $dbOption => $value) {
1395 3
            $builder->setDbOption($dbOption, $value);
1396
        }
1397 3
        $class = get_called_class();
1398 3
        if (!empty($this->_changedParams) && $this->pk()) {
1399 2
            \Inji::$inst->event('modelItemParamsChanged-' . $class, $this);
1400
        }
1401 3
        $this->beforeSave();
1402 3
        $values = [];
1403
1404 3
        foreach (static::$cols as $col => $param) {
1405 3
            if (in_array($col, array_keys($this->_params)) && (!$this->pk() || ($this->pk() && in_array($col, array_keys($this->_changedParams))))) {
1406 3
                $values[$col] = $this->_params[$col];
1407
            }
1408
        }
1409 3
        if (!$this->pk()) {
1410 1
            foreach ($class::$cols as $colName => $params) {
0 ignored issues
show
Bug introduced by
The property cols does not exist on string.
Loading history...
1411 1
                if (isset($params['default']) && !isset($values[$colName])) {
1412 1
                    $this->_params[$colName] = $values[$colName] = $params['default'];
1413
                }
1414
            }
1415
        }
1416
1417 3
        if (empty($values) && empty($options['empty'])) {
1418
            return false;
1419
        }
1420 3
        if (static::$categoryModel) {
1421
            $this->changeItemTree();
1422
        }
1423 3
        if (static::$treeCategory) {
1424
            $this->changeCategoryTree();
1425
        }
1426 3
        $new = !$this->pk();
1427 3
        if ($new) {
1428 1
            $builder->where(static::index(), $builder->insert($values));
1429
        } else {
1430 2
            $builder->where(static::index(), $this->pk());
1431 2
            $builder->update($values);
1432
        }
1433 3
        $this->logChanges($new);
1434 3
        $this->_params = $builder->get(['array' => true]);
0 ignored issues
show
Documentation Bug introduced by
It seems like $builder->get(array('array' => true)) of type false is incompatible with the declared type array of property $_params.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1435 3
        if ($new) {
1436 1
            \Inji::$inst->event('modelCreatedItem-' . get_called_class(), $this);
1437
        }
1438 3
        $this->afterSave();
1439 3
        return $this->pk();
1440
    }
1441
1442
    /**
1443
     * After save trigger
1444
     */
1445 3
    public function afterSave() {
1446
1447 3
    }
1448
1449
    /**
1450
     * Before delete trigger
1451
     */
1452 1
    public function beforeDelete() {
1453
1454 1
    }
1455
1456
    /**
1457
     * Delete item from module storage
1458
     *
1459
     * @param array $options
1460
     * @return boolean
1461
     */
1462
    public function deleteFromModuleStorage($options) {
1463
1464
        $col = static::index();
1465
        $id = $this->pk();
1466
        $appType = '';
1467
        $classPath = explode('\\', get_called_class());
1468
        if (!empty(static::$storage['options']['share'])) {
1469
            $moduleConfig = Config::share($classPath[0]);
1470
        } else {
1471
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
0 ignored issues
show
Bug introduced by
strpos(static::storage['...'], 'system') !== false of type boolean is incompatible with the type Inji\App expected by parameter $app of Inji\Config::module(). ( Ignorable by Annotation )

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

1471
            $moduleConfig = Config::module($classPath[0], /** @scrutinizer ignore-type */ strpos(static::$storage['type'], 'system') !== false);
Loading history...
1472
        }
1473
1474
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1475
            if (empty($options['appType'])) {
1476
                $appType = App::$cur->type;
1477
            } else {
1478
                $appType = $options['appType'];
1479
            }
1480
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1481
        } else {
1482
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1483
        }
1484
        if (empty($storage[$classPath[1]])) {
1485
            $storage[$classPath[1]] = [];
1486
        }
1487
        foreach ($storage[$classPath[1]] as $key => $item) {
1488
1489
            if ($item[$col] == $id) {
1490
                unset($storage[$classPath[1]][$key]);
1491
                break;
1492
            }
1493
        }
1494
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1495
            $moduleConfig['storage'][$appType] = $storage;
1496
        } else {
1497
            $moduleConfig['storage'] = $storage;
1498
        }
1499
        if (empty(static::$storage['options']['share'])) {
1500
            Config::save('module', $moduleConfig, $classPath[0]);
1501
        } else {
1502
            Config::save('share', $moduleConfig, $classPath[0]);
1503
        }
1504
        return true;
1505
    }
1506
1507
    /**
1508
     * Delete item from data base
1509
     *
1510
     * @param array $options
1511
     * @return boolean
1512
     */
1513 1
    public function delete($options = []) {
1514 1
        $this->beforeDelete();
1515 1
        if (!empty($this->pk())) {
1516 1
            $builder = new Builder(get_called_class(), $this->app);
1517 1
            $builder->connection($this->connectionName);
1518 1
            foreach ($this->dbOptions as $dbOption => $value) {
1519 1
                $builder->setDbOption($dbOption, $value);
1520
            }
1521 1
            $builder->where($this->index(), $this->pk());
1522 1
            $result = $builder->delete();
1523 1
            if ($result) {
1524 1
                $this->afterDelete();
1525 1
                return $result;
1526
            }
1527
        }
1528
        return false;
1529
    }
1530
1531
    /**
1532
     * Delete items from data base
1533
     *
1534
     * @param array $where
1535
     */
1536
    public static function deleteList($where = []) {
1537
        if (!empty($where)) {
1538
            static::fixPrefix($where, 'first');
1539
            App::$cur->db->where($where);
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
1540
        }
1541
        App::$cur->db->delete(static::table());
0 ignored issues
show
Bug introduced by
The method delete() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

1541
        App::$cur->db->/** @scrutinizer ignore-call */ 
1542
                       delete(static::table());
Loading history...
1542
    }
1543
1544
    /**
1545
     * After delete trigger
1546
     */
1547 1
    public function afterDelete() {
1548
1549 1
    }
1550
1551
    /**
1552
     * find relation for col name
1553
     *
1554
     * @param string $col
1555
     * @return array|null
1556
     */
1557
    public static function findRelation($col) {
1558
1559
        foreach (static::relations() as $relName => $rel) {
1560
            if ($rel['col'] == $col) {
1561
                return $relName;
1562
            }
1563
        }
1564
        return null;
1565
    }
1566
1567
    /**
1568
     * Set params for model
1569
     *
1570
     * @param array $params
1571
     */
1572 3
    public function setParams($params) {
1573 3
        foreach ($params as $paramName => $value) {
1574 3
            $this->$paramName = $value;
1575
        }
1576 3
    }
1577
1578
    /**
1579
     * Return relation
1580
     *
1581
     * @param string $relName
1582
     * @return array|boolean
1583
     */
1584 1
    public static function getRelation($relName) {
1585 1
        $relations = static::relations();
1586 1
        return !empty($relations[$relName]) ? $relations[$relName] : false;
1587
    }
1588
1589
    /**
1590
     * Load relation
1591
     *
1592
     * @param string $name
1593
     * @param array $params
1594
     * @return null|array|integer|\Model
1595
     */
1596 1
    public function loadRelation($name, $params = []) {
1597 1
        $relation = static::getRelation($name);
1598 1
        if ($relation) {
1599
            if (!isset($relation['type'])) {
1600
                $type = 'to';
1601
            } else {
1602
                $type = $relation['type'];
1603
            }
1604
            $getCol = null;
1605
            $getParams = [];
1606
            switch ($type) {
1607
                case 'relModel':
1608
                    if (!$this->pk()) {
1609
                        return [];
1610
                    }
1611
                    $fixedCol = $relation['model']::index();
1612
                    $relation['relModel']::fixPrefix($fixedCol);
1613
                    $join = [$relation['relModel']::table(), $relation['relModel']::colPrefix() . $this->index() . ' = ' . $this->pk() . ' and ' . $relation['relModel']::colPrefix() . $relation['model']::index() . ' = ' . $relation['model']::index(), 'INNER'];
1614
                    $getType = 'getList';
1615
                    $options = [
1616
                        'cols' => (isset($params['cols'])) ? $params['cols'] : ((isset($relation['cols'])) ? $relation['cols'] : null),
1617
                        'join' => [$join],
1618
                        'where' => (isset($params['where'])) ? $params['where'] : ((isset($relation['where'])) ? $relation['where'] : null),
1619
                        'array' => (!empty($params['array'])) ? true : false,
1620
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1621
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1622
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1623
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1624
                    ];
1625
                    break;
1626
                case 'many':
1627
                    if (!$this->{$this->index()}) {
1628
                        return [];
1629
                    }
1630
                    $getType = 'getList';
1631
                    $options = [
1632
                        'cols' => (isset($params['cols'])) ? $params['cols'] : ((isset($relation['cols'])) ? $relation['cols'] : null),
1633
                        'join' => (isset($relation['join'])) ? $relation['join'] : null,
1634
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1635
                        'array' => (!empty($params['array'])) ? true : false,
1636
                        'forSelect' => !empty($params['forSelect']),
1637
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1638
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1639
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1640
                        'appType' => (isset($params['appType'])) ? $params['appType'] : ((isset($relation['appType'])) ? $relation['appType'] : null),
1641
                        'where' => []
1642
                    ];
1643
                    $options['where'][] = [$relation['col'], $this->{$this->index()}];
1644
                    if (!empty($relation['where'])) {
1645
                        $options['where'] = array_merge($options['where'], [$relation['where']]);
1646
                    }
1647
                    if (!empty($params['where'])) {
1648
                        $options['where'] = array_merge($options['where'], [$params['where']]);
1649
                    }
1650
                    break;
1651
                case 'one':
1652
                    $getType = 'get';
1653
                    $options = [$relation['col'], $this->pk()];
1654
                    break;
1655
                default:
1656
                    if ($this->{$relation['col']} === null) {
1657
                        return null;
1658
                    }
1659
                    $getType = 'get';
1660
                    $options = $this->{$relation['col']};
1661
                    $getParams['appType'] = $this->appType;
1662
            }
1663
            if (!empty($params['count'])) {
1664
                if (class_exists($relation['model'])) {
1665
                    return $relation['model']::getCount($options);
1666
                }
1667
                return 0;
1668
            } else {
1669
                if (class_exists($relation['model'])) {
1670
                    $this->loadedRelations[$name][json_encode($params)] = $relation['model']::$getType($options, $getCol, $getParams);
1671
                } else {
1672
                    $this->loadedRelations[$name][json_encode($params)] = [];
1673
                }
1674
            }
1675
            return $this->loadedRelations[$name][json_encode($params)];
1676
        }
1677 1
        return null;
1678
    }
1679
1680
    /**
1681
     * Add relation item
1682
     *
1683
     * @param string $relName
1684
     * @param \Inji\Model $objectId
1685
     * @return \Inji\Model|boolean
1686
     */
1687
    public function addRelation($relName, $objectId) {
1688
        $relation = $this->getRelation($relName);
1689
        if ($relation) {
1690
            $rel = $relation['relModel']::get([[$relation['model']::index(), $objectId], [$this->index(), $this->pk()]]);
1691
            if (!$rel) {
1692
                $rel = new $relation['relModel']([
1693
                    $relation['model']::index() => $objectId,
1694
                    $this->index() => $this->pk()
1695
                ]);
1696
                $rel->save();
1697
            }
1698
            return $rel;
1699
        }
1700
        return false;
1701
    }
1702
1703
    /**
1704
     * Check user access for form
1705
     *
1706
     * @param string $formName
1707
     * @return boolean
1708
     */
1709
    public function checkFormAccess($formName) {
1710
        if ($formName == 'manage' && !Users\User::$cur->isAdmin()) {
0 ignored issues
show
Bug introduced by
The type Inji\Users\User was not found. Did you mean Users\User? If so, make sure to prefix the type with \.
Loading history...
1711
            return false;
1712
        }
1713
        return true;
1714
    }
1715
1716
    /**
1717
     * Check access for model
1718
     *
1719
     * @param string $mode
1720
     * @param \Users\User $user
1721
     * @return boolean
1722
     */
1723
    public function checkAccess($mode = 'write', $user = null) {
1724
        if (!$user) {
1725
            $user = \Users\User::$cur;
1726
        }
1727
        return $user->isAdmin();
1728
    }
1729
1730
    /**
1731
     * Param and relation with params getter
1732
     *
1733
     * @param string $name
1734
     * @param array $params
1735
     * @return \Value|mixed
0 ignored issues
show
Bug introduced by
The type Value was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1736
     */
1737
    public function __call($name, $params) {
1738
        $fixedName = $name;
1739
        static::fixPrefix($fixedName);
1740
        if (isset($this->_params[$fixedName])) {
1741
            return new Value($this, $fixedName);
1742
        } elseif (isset($this->_params[$name])) {
1743
            return new Value($this, $name);
1744
        } elseif (!empty($params[0]) && isset($this->loadedRelations[$name][json_encode($params[0])])) {
1745
            return $this->loadedRelations[$name][json_encode($params[0])];
1746
        }
1747
        return call_user_func_array([$this, 'loadRelation'], array_merge([$name], $params));
1748
    }
1749
1750
    /**
1751
     * Param and relation getter
1752
     *
1753
     * @param string $name
1754
     * @return mixed
1755
     */
1756 4
    public function __get($name) {
1757 4
        if (isset($this->_params[$name])) {
1758 4
            return $this->_params[$name];
1759
        }
1760 1
        if (isset($this->loadedRelations[$name][json_encode([])])) {
1761
            return $this->loadedRelations[$name][json_encode([])];
1762
        }
1763 1
        return $this->loadRelation($name);
1764
    }
1765
1766
    /**
1767
     * Return model value in object
1768
     *
1769
     * @param string $name
1770
     * @return \Value|null
1771
     */
1772
    public function value($name) {
1773
        $fixedName = $name;
1774
        static::fixPrefix($fixedName);
1775
        if (isset($this->_params[$fixedName])) {
1776
            return new Value($this, $fixedName);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new Inji\Value($this, $fixedName) returns the type Inji\Value which is incompatible with the documented return type null|Value.
Loading history...
1777
        } elseif ($this->_params[$name]) {
1778
            return new Value($this, $name);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new Inji\Value($this, $name) returns the type Inji\Value which is incompatible with the documented return type null|Value.
Loading history...
1779
        }
1780
        return null;
1781
    }
1782
1783
    /**
1784
     * Return manager filters
1785
     *
1786
     * @return array
1787
     */
1788
    public static function managerFilters() {
1789
        return [];
1790
    }
1791
1792
    /**
1793
     * Return validators for cols
1794
     *
1795
     * @return array
1796
     */
1797
    public static function validators() {
1798
        return [];
1799
    }
1800
1801
    /**
1802
     * Return validator by name
1803
     *
1804
     * @param string $name
1805
     * @return array
1806
     */
1807
    public static function validator($name) {
1808
        $validators = static::validators();
1809
        if (!empty($validators[$name])) {
1810
            return $validators[$name];
1811
        }
1812
        return [];
1813
    }
1814
1815
    public function genViewLink() {
1816
        $className = get_class($this);
1817
        $link = substr($className, 0, strpos($className, '\\'));
1818
        $link .= '/view/';
1819
        $link .= str_replace('\\', '%5C', substr($className, strpos($className, '\\') + 1));
1820
        $link .= "/{$this->id}";
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Inji\Model. Since you implemented __get, consider adding a @property annotation.
Loading history...
1821
        return $link;
1822
    }
1823
1824
    public function extract($model) {
1825
        $params = [];
1826
        if (empty($this->_params[$model::index()])) {
1827
            return false;
1828
        }
1829
        $params['id'] = $this->_params[$model::index()];
1830
        $indexes = array_keys($this->_params);
1831
        foreach ($model::$cols as $colName => $colParams) {
1832
            if (in_array($model::colPrefix() . $colName, $indexes)) {
1833
                $params[$model::colPrefix() . $colName] = $this->_params[$model::colPrefix() . $colName];
1834
            }
1835
        }
1836
        if (!$params) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $params of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1837
            return FALSE;
1838
        }
1839
        return new $model($params);
1840
    }
1841
1842
    /**
1843
     * Set handler for model params
1844
     *
1845
     * @param string $name
1846
     * @param mixed $value
1847
     */
1848 3
    public function __set($name, $value) {
1849
        //static::fixPrefix($name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1850 3
        $className = get_called_class();
1851 3
        $shortName = $name;
1852 3
        if (!$value && !empty(static::$cols[$shortName]) && in_array('emptyValue', array_keys(static::$cols[$shortName]))) {
1853
            $value = static::$cols[$shortName]['emptyValue'];
1854
        }
1855 3
        if (is_null($value) && empty(static::$cols[$shortName]['null'])) {
1856
            $value = '';
1857
        }
1858 3
        if (!empty($className::$cols[$shortName])) {
0 ignored issues
show
Bug introduced by
The property cols does not exist on string.
Loading history...
1859 3
            switch ($className::$cols[$shortName]['type']) {
1860
                case 'decimal':
1861
                    $value = (float)$value;
1862
                    break;
1863
                case 'number':
1864
                    $value = (int)$value;
1865
                    break;
1866
                case 'bool':
1867
                    $value = (bool)$value;
1868
                    break;
1869
            }
1870
        }
1871 3
        if (in_array($name, array_keys($this->_params)) && $this->_params[$name] != $value && !in_array($name, array_keys($this->_changedParams))) {
1872 1
            $this->_changedParams[$name] = $this->_params[$name];
1873
        }
1874 3
        $this->_params[$name] = $value;
1875 3
        if (in_array($name, array_keys($this->_params)) && in_array($name, array_keys($this->_changedParams)) && $this->_changedParams[$name] == $value) {
1876
            unset($this->_changedParams[$name]);
1877
        }
1878 3
    }
1879
1880
    /**
1881
     * Isset handler for model params
1882
     *
1883
     * @param string $name
1884
     * @return boolean
1885
     */
1886
    public function __isset($name) {
1887
        static::fixPrefix($name);
1888
        return isset($this->_params[$name]);
1889
    }
1890
1891
    /**
1892
     * Convert object to string
1893
     *
1894
     * @return string
1895
     */
1896
    public function __toString() {
1897
        return $this->name();
1898
    }
1899
}