Completed
Push — master ( 06e648...3fbbc6 )
by Alexey
04:54
created

Model::getRelation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 5
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
/**
4
 * Log
5
 *
6
 * @author Alexey Krupskiy <[email protected]>
7
 * @link http://inji.ru/
8
 * @copyright 2015 Alexey Krupskiy
9
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
10
 */
11
class Model
12
{
13
    /**
14
     * Object storage type
15
     * 
16
     * @var array 
17
     */
18
    public static $storage = ['type' => 'db'];
19
20
    /**
21
     * Object name
22
     * 
23
     * @var string 
24
     */
25
    public static $objectName = '';
26
27
    /**
28
     * App type for separate data storage
29
     * 
30
     * @var string
31
     */
32
    public $appType = 'app';
33
34
    /**
35
     * Object current params
36
     * 
37
     * @var array
38
     */
39
    public $_params = [];
40
41
    /**
42
     * List of changed params in current instance
43
     * 
44
     * @var array
45
     */
46
    public $_changedParams = [];
47
48
    /**
49
     * Loaded relations
50
     * 
51
     * @var array 
52
     */
53
    public $loadedRelations = [];
54
55
    /**
56
     * Model name where this model uses as category
57
     * 
58
     * @var string
59
     */
60
    public static $treeCategory = '';
61
62
    /**
63
     * Model name who uses as category in this model
64
     * 
65
     * @var string
66
     */
67
    public static $categoryModel = '';
68
69
    /**
70
     * Col labels
71
     * 
72
     * @var array
73
     */
74
    public static $labels = [];
75
76
    /**
77
     * Model forms
78
     * 
79
     * @var array
80
     */
81
    public static $forms = [];
82
83
    /**
84
     * Model cols
85
     * 
86
     * @var array
87
     */
88
    public static $cols = [];
89
90
    /**
91
     * Options group for display inforamtion from model
92
     * 
93
     * @var array
94
     */
95
    public static $view = [];
96
97
    /**
98
     * List of relations need loaded with item
99
     * 
100
     * @var array 
101
     */
102
    public static $needJoin = [];
103
104
    /**
105
     * List of joins who need to laod
106
     * 
107
     * @var array 
108
     */
109
    public static $relJoins = [];
110
111
    /**
112
     * Set params when model create
113
     * 
114
     * @param array $params
115
     */
116
    public function __construct($params = [])
117
    {
118
        $this->setParams($params);
119
    }
120
121
    /**
122
     * return object name
123
     * 
124
     * @return string
125
     */
126
    public static function objectName()
127
    {
128
        return static::$objectName;
129
    }
130
131
    /**
132
     * Retrn col value with col params and relations path
133
     * 
134
     * @param Model $object
135
     * @param string $valuePath
136
     * @param boolean $convert
137
     * @param boolean $manageHref
138
     * @return string
139
     */
140
    public static function getColValue($object, $valuePath, $convert = false, $manageHref = false)
141
    {
142
        if (strpos($valuePath, ':')) {
143
            $rel = substr($valuePath, 0, strpos($valuePath, ':'));
144
            $param = substr($valuePath, strpos($valuePath, ':') + 1);
145
            if (!$object->$rel) {
146
                $modelName = get_class($object);
147
                $relations = $modelName::relations();
148
                if (empty($relations[$rel]['type']) || $relations[$rel]['type'] == 'one') {
149
                    return $object->{$relations[$rel]['col']};
150
                }
151
                return 0;
152
            }
153
            if (strpos($valuePath, ':')) {
154
                return self::getColValue($object->$rel, $param, $convert, $manageHref);
155
            } else {
156
                return $convert ? Model::resloveTypeValue($object->$rel, $param, $manageHref) : $object->$rel->$param;
157
            }
158
        } else {
159
            return $convert ? Model::resloveTypeValue($object, $valuePath, $manageHref) : $object->$valuePath;
160
        }
161
    }
162
163
    /**
164
     * Retrun value for view
165
     * 
166
     * @param Model $item
167
     * @param string $colName
168
     * @param boolean $manageHref
169
     * @return string
170
     */
171
    public static function resloveTypeValue($item, $colName, $manageHref = false)
172
    {
173
        $modelName = get_class($item);
174
        $colInfo = $modelName::getColInfo($colName);
175
        $type = !empty($colInfo['colParams']['type']) ? $colInfo['colParams']['type'] : 'string';
176
        $value = '';
177
        switch ($type) {
178
            case 'select':
179
                switch ($colInfo['colParams']['source']) {
180
                    case 'model':
181
                        $sourceValue = '';
182
                        if ($item->$colName) {
183
                            $sourceValue = $colInfo['colParams']['model']::get($item->$colName);
184
                        }
185
                        $value = $sourceValue ? $sourceValue->name() : 'Не задано';
186
                        break;
187
                    case 'array':
188
                        $value = !empty($colInfo['colParams']['sourceArray'][$item->$colName]) ? $colInfo['colParams']['sourceArray'][$item->$colName] : 'Не задано';
189
                        if (is_array($value) && $value['text']) {
190
                            $value = $value['text'];
191
                        }
192
                        break;
193
                    case 'bool':
194
                        return $item->$colName ? 'Да' : 'Нет';
195 View Code Duplication
                    case 'method':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
                        if (!empty($colInfo['colParams']['params'])) {
197
                            $values = call_user_func_array([App::$cur->$colInfo['colParams']['module'], $colInfo['colParams']['method']], $colInfo['colParams']['params']);
198
                        } else {
199
                            $values = $colInfo['colParams']['module']->$colInfo['colParams']['method']();
200
                        }
201
                        $value = !empty($values[$item->$colName]) ? $values[$item->$colName] : 'Не задано';
202
                        break;
203
                    case 'void':
204
                        if (!empty($modelName::$cols[$colName]['value']['type']) && $modelName::$cols[$colName]['value']['type'] == 'moduleMethod') {
205
                            return \App::$cur->{$modelName::$cols[$colName]['value']['module']}->{$modelName::$cols[$colName]['value']['method']}($item, $colName, $modelName::$cols[$colName]);
206
                        }
207
                        break;
208
                    case 'relation':
209
                        $relations = $colInfo['modelName']::relations();
210
                        $relValue = $relations[$colInfo['colParams']['relation']]['model']::get($item->$colName);
211
                        $relModel = $relations[$colInfo['colParams']['relation']]['model'];
212
                        $relModel = strpos($relModel, '\\') === 0 ? substr($relModel, 1) : $relModel;
213
                        if ($manageHref) {
214
                            $value = $relValue ? "<a href='/admin/" . str_replace('\\', '/view/', $relModel) . "/" . $relValue->pk() . "'>" . $relValue->name() . "</a>" : 'Не задано';
215
                        } else {
216
                            $value = $relValue ? $relValue->name() : 'Не задано';
217
                        }
218
                        break;
219
                }
220
                break;
221 View Code Duplication
            case 'image':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
                $file = Files\File::get($item->$colName);
223
                if ($file) {
224
                    $value = '<img src="' . $file->path . '?resize=60x120" />';
225
                } else {
226
                    $value = '<img src="/static/system/images/no-image.png?resize=60x120" />';
227
                }
228
                break;
229
            case 'bool':
230
                $value = $item->$colName ? 'Да' : 'Нет';
231
                break;
232
            case 'void':
233
                if (!empty($colInfo['colParams']['value']['type']) && $colInfo['colParams']['value']['type'] == 'moduleMethod') {
234
                    return \App::$cur->{$colInfo['colParams']['value']['module']}->{$colInfo['colParams']['value']['method']}($item, $colName, $colInfo['colParams']);
235
                }
236
                break;
237
            default:
238
                $value = $item->$colName;
239
                break;
240
        }
241
        return $value;
242
    }
243
244
    /**
245
     * Fix col prefix
246
     * 
247
     * @param mixed $array
248
     * @param string $searchtype
249
     * @param string $rootModel
250
     * @return null
251
     */
252
    public static function fixPrefix(&$array, $searchtype = 'key', $rootModel = '')
253
    {
254
        if (!$rootModel) {
255
            $rootModel = get_called_class();
256
        }
257
        $cols = static::cols();
258
        if (!$array) {
259
            return;
260
        }
261
        if (!is_array($array)) {
262 View Code Duplication
            if (!isset($cols[static::colPrefix() . $array]) && isset(static::$cols[$array])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
                static::createCol($array);
264
                $cols = static::cols(true);
265
            }
266
            if (!isset($cols[$array]) && isset($cols[static::colPrefix() . $array])) {
267
                $array = static::colPrefix() . $array;
268
            } else {
269
                static::checkForJoin($array, $rootModel);
270
            }
271
            return;
272
        }
273
        switch ($searchtype) {
274
            case 'key':
275
                foreach ($array as $key => $item) {
276 View Code Duplication
                    if (!isset($cols[static::colPrefix() . $key]) && isset(static::$cols[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
                        static::createCol($key);
278
                        $cols = static::cols(true);
279
                    }
280
                    if (!isset($cols[$key]) && isset($cols[static::colPrefix() . $key])) {
281
                        $array[static::colPrefix() . $key] = $item;
282
                        unset($array[$key]);
283
                        $key = static::colPrefix() . $key;
284
                    }
285
                    if (is_array($array[$key])) {
286
                        static::fixPrefix($array[$key], 'key', $rootModel);
287
                    } else {
288
                        static::checkForJoin($key, $rootModel);
289
                    }
290
                }
291
                break;
292
            case 'first':
293
                if (isset($array[0]) && is_string($array[0])) {
294
                    if (!isset($cols[static::colPrefix() . $array[0]]) && isset(static::$cols[$array[0]])) {
295
                        static::createCol($array[0]);
296
                        $cols = static::cols(true);
297
                    }
298 View Code Duplication
                    if (!isset($cols[$array[0]]) && isset($cols[static::colPrefix() . $array[0]])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
                        $array[0] = static::colPrefix() . $array[0];
300
                    } else {
301
                        static::checkForJoin($array[0], $rootModel);
302
                    }
303
                } elseif (isset($array[0]) && is_array($array[0])) {
304
                    foreach ($array as &$item) {
305
                        static::fixPrefix($item, 'first', $rootModel);
306
                    }
307
                }
308
                break;
309
        }
310
    }
311
312
    /**
313
     * Check model relations path and load need relations
314
     * 
315
     * @param string $col
316
     * @param string $rootModel
317
     */
318
    public static function checkForJoin(&$col, $rootModel)
319
    {
320
321
        if (strpos($col, ':') !== false) {
322
            $relations = static::relations();
323
            if (isset($relations[substr($col, 0, strpos($col, ':'))])) {
324
                $rel = substr($col, 0, strpos($col, ':'));
325
                $col = substr($col, strpos($col, ':') + 1);
326
327
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
328
                switch ($type) {
329 View Code Duplication
                    case 'to':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
330
                        $relCol = $relations[$rel]['col'];
331
                        static::fixPrefix($relCol);
332
                        $rootModel::$relJoins[$relations[$rel]['model'] . '_' . $rel] = [$relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol];
333
                        break;
334
                    case 'one':
335 View Code Duplication
                    case 'many':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
                        $relCol = $relations[$rel]['col'];
337
                        $relations[$rel]['model']::fixPrefix($relCol);
338
                        $rootModel::$relJoins[$relations[$rel]['model'] . '_' . $rel] = [$relations[$rel]['model']::table(), static::index() . ' = ' . $relCol];
339
                        break;
340
                }
341
                $relations[$rel]['model']::fixPrefix($col, 'key', $rootModel);
342
            }
343
        }
344
    }
345
346
    /**
347
     * Return full col information
348
     * 
349
     * @param string $col
350
     * @return array
351
     */
352
    public static function getColInfo($col)
353
    {
354
        return static::parseColRecursion($col);
355
    }
356
357
    /**
358
     * Information extractor for col relations path
359
     * 
360
     * @param string|array $info
361
     * @return array
362
     */
363
    public static function parseColRecursion($info)
364
    {
365
        if (is_string($info)) {
366
            $info = ['col' => $info, 'rawCol' => $info, 'modelName' => '', 'label' => [], 'joins' => []];
367
        }
368
        if (strpos($info['col'], ':') !== false) {
369
            $relations = static::relations();
370
            if (isset($relations[substr($info['col'], 0, strpos($info['col'], ':'))])) {
371
                $rel = substr($info['col'], 0, strpos($info['col'], ':'));
372
                $info['col'] = substr($info['col'], strpos($info['col'], ':') + 1);
373
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
374
                switch ($type) {
375 View Code Duplication
                    case 'to':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
376
                        $relCol = $relations[$rel]['col'];
377
                        static::fixPrefix($relCol);
378
                        //$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...
379
                        $info['joins'][$relations[$rel]['model'] . '_' . $rel] = [$relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol];
380
                        break;
381 View Code Duplication
                    case 'one':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
382
                        $relCol = $relations[$rel]['col'];
383
                        $relations[$rel]['model']::fixPrefix($relCol);
384
                        //$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...
385
                        $info['joins'][$relations[$rel]['model'] . '_' . $rel] = [$relations[$rel]['model']::table(), static::index() . ' = ' . $relCol];
386
                        break;
387
                }
388
                $info = $relations[$rel]['model']::parseColRecursion($info);
389
            }
390
        } else {
391
            $cols = static::cols();
392
            if (!empty(static::$labels[$info['col']])) {
393
                $info['label'] = static::$labels[$info['col']];
394
            }
395
396
            if (isset(static::$cols[$info['col']])) {
397
                $info['colParams'] = static::$cols[$info['col']];
398
            } elseif (isset(static::$cols[str_replace(static::colPrefix(), '', $info['col'])])) {
399
                $info['colParams'] = static::$cols[str_replace(static::colPrefix(), '', $info['col'])];
400
            } else {
401
                $info['colParams'] = [];
402
            }
403 View Code Duplication
            if (!isset($cols[$info['col']]) && isset($cols[static::colPrefix() . $info['col']])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
404
                $info['col'] = static::colPrefix() . $info['col'];
405
            }
406
            $info['modelName'] = get_called_class();
407
        }
408
        return $info;
409
    }
410
411
    /**
412
     * Return actual cols from data base
413
     * 
414
     * @param boolean $refresh
415
     * @return array
416
     */
417
    public static function cols($refresh = false)
418
    {
419
        if (static::$storage['type'] == 'moduleConfig') {
420
            return [];
421
        }
422 View Code Duplication
        if (empty(Model::$cols[static::table()]) || $refresh) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
            Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
424
        }
425 View Code Duplication
        if (!Model::$cols[static::table()]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
426
            static::createTable();
427
            Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
428
        }
429
        return Model::$cols[static::table()];
430
    }
431
432
    /**
433
     * Return cols indexes for create tables
434
     * 
435
     * @return array
436
     */
437
    public static function indexes()
438
    {
439
        return [];
440
    }
441
442
    /**
443
     * Generate params string for col by name
444
     * 
445
     * @param string $colName
446
     * @return boolean|string
447
     */
448
    public static function genColParams($colName)
449
    {
450
        if (empty(static::$cols[$colName]) || static::$storage['type'] == 'moduleConfig') {
451
            return false;
452
        }
453
454
        $params = false;
455
        switch (static::$cols[$colName]['type']) {
456
            case 'select':
457
                switch (static::$cols[$colName]['source']) {
458
                    case 'relation':
459
                        $params = 'int(11) UNSIGNED NOT NULL';
460
                        break;
461
                    default:
462
                        $params = 'varchar(255) NOT NULL';
463
                }
464
                break;
465
            case 'image':
466
                $params = 'int(11) UNSIGNED NOT NULL';
467
                break;
468
            case 'number':
469
                $params = 'int(11) NOT NULL';
470
                break;
471
            case 'text':
472
            case 'email':
473
                $params = 'varchar(255) NOT NULL';
474
                break;
475
            case 'html':
476
            case 'textarea':
477
            case 'json':
478
            case 'password':
479
            case 'dynamicType':
480
                $params = 'text NOT NULL';
481
                break;
482
            case 'bool':
483
                $params = 'tinyint(1) UNSIGNED NOT NULL';
484
                break;
485
            case 'decimal':
486
                $params = 'decimal(8, 2) NOT NULL';
487
                break;
488
            case 'date':
489
                $params = 'date NOT NULL DEFAULT 0';
490
                break;
491
            case 'dateTime':
492
                $params = 'timestamp NOT NULL DEFAULT 0';
493
                break;
494
        }
495
        return $params;
496
    }
497
498
    /**
499
     * Create new col in data base
500
     * 
501
     * @param string $colName
502
     * @return boolean|integer
503
     */
504
    public static function createCol($colName)
505
    {
506
        $params = static::genColParams($colName);
507
        if ($params === false) {
508
            return false;
509
        }
510
        return App::$cur->db->addCol(static::table(), static::colPrefix() . $colName, $params);
511
    }
512
513
    public static function createTable()
514
    {
515
        if (static::$storage['type'] == 'moduleConfig') {
516
            return true;
517
        }
518
        if (!App::$cur->db) {
519
            return false;
520
        }
521
522
        $query = App::$cur->db->newQuery();
523
        if (!$query) {
524
            return false;
525
        }
526
527
        if (!isset($this)) {
528
            $tableName = static::table();
529
            $colPrefix = static::colPrefix();
530
            $indexes = static::indexes();
531
        } else {
532
            $tableName = $this->table();
533
            $colPrefix = $this->colPrefix();
534
            $indexes = $this->indexes();
535
        }
536
        if (App::$cur->db->tableExist($tableName)) {
537
            return true;
538
        }
539
        $cols = [
540
            $colPrefix . 'id' => 'pk'
541
        ];
542
        $className = get_called_class();
543
        if (!empty($className::$cols)) {
544
            foreach ($className::$cols as $colName => $colParams) {
545
                if ($colName == 'date_create') {
546
                    $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
547
                    continue;
548
                }
549
                $params = $className::genColParams($colName);
550
                if ($params) {
551
                    $cols[$colPrefix . $colName] = $params;
552
                }
553
            }
554
        }
555
        if (empty($cols[$colPrefix . 'date_create'])) {
556
            $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
557
        }
558
        $tableIndexes = [];
559
        if ($indexes) {
560
            foreach ($indexes as $indexName => $index) {
561
                $tableIndexes[] = $index['type'] . ' ' . App::$cur->db->table_prefix . $indexName . ' (' . implode(',', $index['cols']) . ')';
562
            }
563
        }
564
565
        $query->createTable($tableName, $cols, $tableIndexes);
566
        return true;
567
    }
568
569
    /**
570
     * Return table name
571
     * 
572
     * @return string
573
     */
574
    public static function table()
575
    {
576
        return strtolower(str_replace('\\', '_', get_called_class()));
577
    }
578
579
    /**
580
     * Return table index col name
581
     * 
582
     * @return string
583
     */
584
    public static function index()
585
    {
586
587
        return static::colPrefix() . 'id';
588
    }
589
590
    /**
591
     * Return col prefix
592
     * 
593
     * @return string
594
     */
595
    public static function colPrefix()
596
    {
597
        $classPath = explode('\\', get_called_class());
598
        $classPath = array_slice($classPath, 1);
599
        return strtolower(implode('_', $classPath)) . '_';
600
    }
601
602
    /**
603
     * return relations list
604
     * 
605
     * @return array
606
     */
607
    public static function relations()
608
    {
609
        return [];
610
    }
611
612
    /**
613
     * Return name of col with object name
614
     * 
615
     * @return string
616
     */
617
    public static function nameCol()
618
    {
619
        return 'name';
620
    }
621
622
    /**
623
     * Return object name
624
     * 
625
     * @return string
626
     */
627
    public function name()
628
    {
629
        return $this->{$this->nameCol()} ? $this->{$this->nameCol()} : $this->pk();
630
    }
631
632
    /**
633
     * Get single object from data base
634
     * 
635
     * @param mixed $param
636
     * @param string $col
637
     * @param array $options
638
     * @return boolean|\Model
639
     */
640
    public static function get($param, $col = null, $options = [])
641
    {
642
        if (static::$storage['type'] == 'moduleConfig') {
643
            return static::getFromModuleStorage($param, $col, $options);
644
        }
645
        if (!empty($col)) {
646
            static::fixPrefix($col);
647
        }
648
649
        if (is_array($param)) {
650
            static::fixPrefix($param, 'first');
651
        }
652
        foreach (static::$relJoins as $join) {
653
            App::$cur->db->join($join[0], $join[1]);
654
        }
655
        static::$relJoins = [];
656
        foreach (static::$needJoin as $rel) {
657
            $relations = static::relations();
658
            if (isset($relations[$rel])) {
659
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
660
                switch ($type) {
661
                    case 'to':
662
                        $relCol = $relations[$rel]['col'];
663
                        static::fixPrefix($relCol);
664
                        App::$cur->db->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
665
                        break;
666
                    case 'one':
667
                        $col = $relations[$rel]['col'];
668
                        $relations[$rel]['model']::fixPrefix($col);
669
                        App::$cur->db->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
670
                        break;
671
                }
672
            }
673
        }
674
        static::$needJoin = [];
675
        if (is_array($param)) {
676
            App::$cur->db->where($param);
677
        } else {
678
            if ($col === null) {
679
680
                $col = static::index();
681
            }
682
            if ($param !== null) {
683
                $cols = static::cols();
684
                if (!isset($cols[$col]) && isset($cols[static::colPrefix() . $col])) {
685
                    $col = static::colPrefix() . $col;
686
                }
687
                App::$cur->db->where($col, $param);
688
            } else {
689
                return false;
690
            }
691
        }
692
        if (!App::$cur->db->where) {
693
            return false;
694
        }
695
        try {
696
            $result = App::$cur->db->select(static::table());
697
        } catch (PDOException $exc) {
698
            if ($exc->getCode() == '42S02') {
699
                static::createTable();
700
            }
701
            $result = App::$cur->db->select(static::table());
702
        }
703
        if (!$result) {
704
            return false;
705
        }
706
        return $result->fetch(get_called_class());
707
    }
708
709
    /**
710
     * Old method
711
     * 
712
     * @param type $options
713
     * @return Array
714
     */
715
    public static function get_list($options = [])
716
    {
717
        $query = App::$cur->db->newQuery();
718
        if (!$query) {
719
            return [];
720
        }
721
        if (!empty($options['where']))
722
            $query->where($options['where']);
723
        if (!empty($options['group'])) {
724
            $query->group($options['group']);
725
        }
726
        if (!empty($options['order']))
727
            $query->order($options['order']);
728
        if (!empty($options['join']))
729
            $query->join($options['join']);
730
        if (!empty($options['distinct']))
731
            $query->distinct = $options['distinct'];
732
733
        foreach (static::$relJoins as $join) {
734
            $query->join($join[0], $join[1]);
735
        }
736
        static::$relJoins = [];
737 View Code Duplication
        foreach (static::$needJoin as $rel) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
738
            $relations = static::relations();
739
            if (isset($relations[$rel])) {
740
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
741
                switch ($type) {
742
                    case 'to':
743
                        $relCol = $relations[$rel]['col'];
744
                        static::fixPrefix($relCol);
745
                        $query->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
746
                        break;
747
                    case 'one':
748
                        $col = $relations[$rel]['col'];
749
                        $relations[$rel]['model']::fixPrefix($col);
750
                        $query->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
751
                        break;
752
                }
753
            }
754
        }
755
        static::$needJoin = [];
756
757 View Code Duplication
        if (!empty($options['limit']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
758
            $limit = (int) $options['limit'];
759
        else {
760
            $limit = 0;
761
        }
762 View Code Duplication
        if (!empty($options['start']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
763
            $start = (int) $options['start'];
764
        else {
765
            $start = 0;
766
        }
767
        if ($limit || $start) {
768
            $query->limit($start, $limit);
769
        }
770
        if (isset($options['key'])) {
771
            $key = $options['key'];
772
        } else {
773
            $key = static::index();
774
        }
775
        try {
776
            $result = $query->select(static::table());
777
        } catch (PDOException $exc) {
778
            if ($exc->getCode() == '42S02') {
779
                static::createTable();
780
            }
781
            $result = $query->select(static::table());
782
        }
783
784
        if (!empty($options['array'])) {
785
            return $result->getArray($key);
786
        }
787
        $list = $result->getObjects(get_called_class(), $key);
788 View Code Duplication
        if (!empty($options['forSelect'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
789
            $return = [];
790
            foreach ($list as $key => $item) {
791
                $return[$key] = $item->name();
792
            }
793
            return $return;
794
        }
795
        return $list;
796
    }
797
798
    /**
799
     * Return list of objects from data base
800
     * 
801
     * @param type $options
802
     * @return type
803
     */
804
    public static function getList($options = [])
805
    {
806
        if (static::$storage['type'] != 'db') {
807
            return static::getListFromModuleStorage($options);
808
        }
809
        if (!empty($options['where'])) {
810
            static::fixPrefix($options['where'], 'first');
811
        }
812
        if (!empty($options['order'])) {
813
            static::fixPrefix($options['order'], 'first');
814
        }
815
        return static::get_list($options);
816
    }
817
818
    /**
819
     * Get single item from module storage
820
     * 
821
     * @param array $param
822
     * @param string $col
823
     * @param array $options
824
     * @return boolean|\Model
825
     */
826
    public static function getFromModuleStorage($param = null, $col = null, $options = [])
827
    {
828
        if ($col === null) {
829
830
            $col = static::index();
831
        }
832
        if ($param == null) {
833
            return false;
834
        }
835
        $classPath = explode('\\', get_called_class());
836 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
837
            $moduleConfig = Config::share($classPath[0]);
838
        } else {
839
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
840
        }
841
        $appType = App::$cur->type;
842 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
843
            if (!empty($options['appType'])) {
844
                $appType = $options['appType'];
845
            }
846
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
847
        } else {
848
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
849
        }
850
        if (!empty($storage[$classPath[1]])) {
851
            $items = $storage[$classPath[1]];
852
            $class = get_called_class();
853
            foreach ($items as $key => $item) {
854
                if ($item[$col] == $param) {
855
                    if (!empty($options['array'])) {
856
                        return $item;
857
                    }
858
                    $item = new $class($item);
859
                    $item->appType = $appType;
860
                    return $item;
861
                }
862
            }
863
        }
864
        return false;
865
    }
866
867
    /**
868
     * Return list items from module storage
869
     * 
870
     * @param array $options
871
     * @return array
872
     */
873
    public static function getListFromModuleStorage($options = [])
874
    {
875
        $classPath = explode('\\', get_called_class());
876 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
877
            $moduleConfig = Config::share($classPath[0]);
878
        } else {
879
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
880
        }
881 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
882
            if (empty($options['appType'])) {
883
                $appType = App::$cur->type;
884
            } else {
885
                $appType = $options['appType'];
886
            }
887
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
888
        } else {
889
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
890
        }
891
        if (!empty($storage[$classPath[1]])) {
892
            $items = [];
893
            $class = get_called_class();
894
            if (isset($options['key'])) {
895
                $arrayKey = $options['key'];
896
            } else {
897
                $arrayKey = static::index();
898
            }
899
            foreach ($storage[$classPath[1]] as $key => $item) {
900
                if (!empty($options['where']) && !Model::checkWhere($item, $options['where'])) {
901
                    continue;
902
                }
903
                $items[$item[$arrayKey]] = new $class($item);
904
            }
905
            if (!empty($options['order'])) {
906
                usort($items, function($a, $b) use($options) {
907
                    if ($a->{$options['order'][0]} > $b->{$options['order'][0]} && $options['order'][1] = 'asc') {
908
                        return 1;
909
                    } elseif ($a->{$options['order'][0]} < $b->{$options['order'][0]} && $options['order'][1] = 'asc') {
910
                        return -1;
911
                    }
912
                    return 0;
913
                });
914
            }
915 View Code Duplication
            if (!empty($options['forSelect'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
916
                $return = [];
917
                foreach ($items as $key => $item) {
918
                    $return[$key] = $item->name();
919
                }
920
                return $return;
921
            }
922
            return $items;
923
        }
924
        return [];
925
    }
926
927
    /**
928
     * Return count of records from module storage
929
     * 
930
     * @param array $options
931
     * @return int
932
     */
933
    public static function getCountFromModuleStorage($options = [])
934
    {
935
936
        $classPath = explode('\\', get_called_class());
937
        $count = 0;
938
        if (empty($options['appType'])) {
939
            $appType = App::$cur->type;
940
        } else {
941
            $appType = $options['appType'];
942
        }
943 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
944
            $moduleConfig = Config::share($classPath[0]);
945
        } else {
946
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
947
        }
948
        if (!empty($moduleConfig['storage'][$appType][$classPath[1]])) {
949
            $items = $moduleConfig['storage'][$appType][$classPath[1]];
950
            if (empty($options['where'])) {
951
                return count($items);
952
            }
953
            foreach ($items as $key => $item) {
954
                if (!empty($options['where'])) {
955
                    if (Model::checkWhere($item, $options['where'])) {
956
                        $count++;
957
                    }
958
                } else {
959
                    $count++;
960
                }
961
            }
962
        }
963
        return $count;
964
    }
965
966
    /**
967
     * Check where for module storage query
968
     * 
969
     * @param array $item
970
     * @param array|string $where
971
     * @param string $value
972
     * @param string $operation
973
     * @param string $concatenation
974
     * @return boolean
975
     */
976
    public static function checkWhere($item = [], $where = '', $value = '', $operation = '=', $concatenation = 'AND')
977
    {
978
979
        if (is_array($where)) {
980
            if (is_array($where[0])) {
981
                foreach ($where as $whereItem) {
982
                    $result = forward_static_call_array(['Model', 'checkWhere'], array_merge([$item], $whereItem));
983
                    if (!$result) {
984
                        return false;
985
                    }
986
                }
987
                return true;
988
            } else {
989
                return forward_static_call_array(['Model', 'checkWhere'], array_merge([$item], $where));
990
            }
991
        }
992
993
        if (!isset($item[$where]) && !$value) {
994
            return true;
995
        }
996
        if (!isset($item[$where]) && $value) {
997
            return false;
998
        }
999
        if ($item[$where] == $value) {
1000
            return true;
1001
        }
1002
        return false;
1003
    }
1004
1005
    /**
1006
     * Return count of records from data base
1007
     * 
1008
     * @param array $options
1009
     * @return array|int
1010
     */
1011
    public static function getCount($options = [])
1012
    {
1013
        if (static::$storage['type'] == 'moduleConfig') {
1014
            return static::getCountFromModuleStorage($options);
1015
        }
1016
        $query = App::$cur->db->newQuery();
1017
        if (!$query) {
1018
            return 0;
1019
        }
1020
        if (!empty($options['where'])) {
1021
            static::fixPrefix($options['where'], 'first');
1022
        }
1023
        if (!empty($options['where']))
1024
            $query->where($options['where']);
1025
        if (!empty($options['join']))
1026
            $query->join($options['join']);
1027
        if (!empty($options['order'])) {
1028
            $query->order($options['order']);
1029
        }
1030 View Code Duplication
        if (!empty($options['limit']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1031
            $limit = (int) $options['limit'];
1032
        else {
1033
            $limit = 0;
1034
        }
1035 View Code Duplication
        if (!empty($options['start']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1036
            $start = (int) $options['start'];
1037
        else {
1038
            $start = 0;
1039
        }
1040
        if ($limit || $start) {
1041
            $query->limit($start, $limit);
1042
        }
1043
1044
        foreach (static::$relJoins as $join) {
1045
            $query->join($join[0], $join[1]);
1046
        }
1047
        static::$relJoins = [];
1048 View Code Duplication
        foreach (static::$needJoin as $rel) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1049
            $relations = static::relations();
1050
            if (isset($relations[$rel])) {
1051
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
1052
                switch ($type) {
1053
                    case 'to':
1054
                        $relCol = $relations[$rel]['col'];
1055
                        static::fixPrefix($relCol);
1056
                        $query->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
1057
                        break;
1058
                    case 'one':
1059
                        $col = $relations[$rel]['col'];
1060
                        $relations[$rel]['model']::fixPrefix($col);
1061
                        $query->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
1062
                        break;
1063
                }
1064
            }
1065
        }
1066
        static::$needJoin = [];
1067
        $cols = 'COUNT(';
1068
1069
        if (!empty($options['distinct'])) {
1070
            if (is_bool($options['distinct'])) {
1071
                $cols .= 'DISTINCT *';
1072
            } else {
1073
                $cols .= "DISTINCT {$options['distinct']}";
1074
            }
1075
        } else {
1076
            $cols .= '*';
1077
        }
1078
        $cols .=') as `count`' . (!empty($options['cols']) ? ',' . $options['cols'] : '');
1079
        $query->cols = $cols;
1080
        if (!empty($options['group'])) {
1081
            $query->group($options['group']);
1082
        }
1083
        try {
1084
            $result = $query->select(static::table());
1085
        } catch (PDOException $exc) {
1086
            if ($exc->getCode() == '42S02') {
1087
                static::createTable();
1088
            }
1089
            $result = $query->select(static::table());
1090
        }
1091
        if (!empty($options['group'])) {
1092
            $count = $result->getArray();
1093
            return $count;
1094
        } else {
1095
            $count = $result->fetch();
1096
            return $count['count'];
1097
        }
1098
    }
1099
1100
    /**
1101
     * Update records in data base
1102
     * 
1103
     * @param array $params
1104
     * @param array $where
1105
     * @return boolean
1106
     */
1107
    public static function update($params, $where = [])
1108
    {
1109
        static::fixPrefix($params);
1110
1111
        $cols = self::cols();
1112
1113
        $values = [];
1114
        foreach ($cols as $col => $param) {
1115
            if (isset($params[$col]))
1116
                $values[$col] = $params[$col];
1117
        }
1118
        if (empty($values)) {
1119
            return false;
1120
        }
1121
1122
        if (!empty($where)) {
1123
            static::fixPrefix($where, 'key');
1124
1125
            App::$cur->db->where($where);
1126
        }
1127
        App::$cur->db->update(static::table(), $values);
1128
    }
1129
1130
    /**
1131
     * Return primary key of object
1132
     * 
1133
     * @return mixed
1134
     */
1135
    public function pk()
1136
    {
1137
        return $this->{$this->index()};
1138
    }
1139
1140
    /**
1141
     * Before save trigger
1142
     */
1143
    public function beforeSave()
1144
    {
1145
        
1146
    }
1147
1148
    /**
1149
     * Save object to module storage
1150
     * 
1151
     * @param array $options
1152
     * @return boolean
1153
     */
1154
    public function saveModuleStorage($options)
1155
    {
1156
1157
        $col = static::index();
1158
        $id = $this->pk();
1159
        $appType = '';
1160
        $classPath = explode('\\', get_called_class());
1161
1162 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1163
            $moduleConfig = Config::share($classPath[0]);
1164
        } else {
1165
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
1166
        }
1167
1168 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1169
            if (empty($options['appType'])) {
1170
                $appType = App::$cur->type;
1171
            } else {
1172
                $appType = $options['appType'];
1173
            }
1174
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1175
        } else {
1176
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1177
        }
1178
        if (empty($storage[$classPath[1]])) {
1179
            $storage[$classPath[1]] = [];
1180
        }
1181
        if ($id) {
1182 View Code Duplication
            foreach ($storage[$classPath[1]] as $key => $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1183
                if ($item[$col] == $id) {
1184
                    $storage[$classPath[1]][$key] = $this->_params;
1185
                    break;
1186
                }
1187
            }
1188
        } else {
1189
            $id = !empty($storage['scheme'][$classPath[1]]['ai']) ? $storage['scheme'][$classPath[1]]['ai'] : 1;
1190
            $this->$col = $id;
1191
            $storage['scheme'][$classPath[1]]['ai'] = $id + 1;
1192
            $storage[$classPath[1]][] = $this->_params;
1193
        }
1194 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1195
            $moduleConfig['storage'][$appType] = $storage;
1196
        } else {
1197
            $moduleConfig['storage'] = $storage;
1198
        }
1199 View Code Duplication
        if (empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1200
            Config::save('module', $moduleConfig, $classPath[0]);
1201
        } else {
1202
            Config::save('share', $moduleConfig, $classPath[0]);
1203
        }
1204
        return true;
1205
    }
1206
1207
    /**
1208
     * Update tree path category
1209
     */
1210
    public function changeCategoryTree()
1211
    {
1212
        $class = get_class($this);
1213
        $itemModel = $class::$treeCategory;
1214
        $oldPath = $this->tree_path;
1215
        $this->tree_path = $this->getCatalogTree($this);
1216
        $itemsTable = \App::$cur->db->table_prefix . $itemModel::table();
1217
        $itemTreeCol = $itemModel::colPrefix() . 'tree_path';
1218
1219
        $categoryTreeCol = $this->colPrefix() . 'tree_path';
1220
        $categoryTable = \App::$cur->db->table_prefix . $this->table();
1221
        if ($oldPath) {
1222
            \App::$cur->db->query('UPDATE
1223
                ' . $categoryTable . ' 
1224
                    SET 
1225
                        ' . $categoryTreeCol . ' = REPLACE(' . $categoryTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $this->tree_path . $this->id . '/' . '") 
1226
                    WHERE ' . $categoryTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1227
1228
            \App::$cur->db->query('UPDATE
1229
                ' . $itemsTable . '
1230
                    SET 
1231
                        ' . $itemTreeCol . ' = REPLACE(' . $itemTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $this->tree_path . $this->id . '/' . '") 
1232
                    WHERE ' . $itemTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1233
        }
1234
        $itemModel::update([$itemTreeCol => $this->tree_path . $this->id . '/'], [$itemModel::colPrefix() . $this->index(), $this->id]);
1235
    }
1236
1237
    /**
1238
     * Return tree path
1239
     * 
1240
     * @param \Model $catalog
1241
     * @return string
1242
     */
1243
    public function getCatalogTree($catalog)
1244
    {
1245
        $catalogClass = get_class($catalog);
1246
        $catalogParent = $catalogClass::get($catalog->parent_id);
1247
        if ($catalog && $catalogParent) {
1248
            if ($catalogParent->tree_path) {
1249
                return $catalogParent->tree_path . $catalogParent->id . '/';
1250
            } else {
1251
                return $this->getCatalogTree($catalogParent) . $catalogParent->id . '/';
1252
            }
1253
        }
1254
        return '/';
1255
    }
1256
1257
    /**
1258
     * Update tree path item
1259
     */
1260
    public function changeItemTree()
1261
    {
1262
        $class = get_class($this);
1263
        $categoryModel = $class::$categoryModel;
1264
        $category = $categoryModel::get($this->{$categoryModel::index()});
1265
        if ($category) {
1266
            $this->tree_path = $category->tree_path . $category->pk() . '/';
1267
        } else {
1268
            $this->tree_path = '/';
1269
        }
1270
    }
1271
1272
    /**
1273
     * Save object to data base
1274
     * 
1275
     * @param array $options
1276
     * @return boolean|int
1277
     */
1278
    public function save($options = [])
1279
    {
1280
1281
        if (static::$storage['type'] == 'moduleConfig') {
1282
            return static::saveModuleStorage($options);
1283
        }
1284
        $class = get_class($this);
1285
        if ($class::$categoryModel) {
1286
            $this->changeItemTree();
1287
        }
1288
        if ($class::$treeCategory) {
1289
            $this->changeCategoryTree();
1290
        }
1291
        if (!empty($this->_changedParams) && $this->pk()) {
1292
            Inji::$inst->event('modelItemParamsChanged-' . get_called_class(), $this);
1293
        }
1294
        $this->beforeSave();
1295
1296
        $values = [];
1297
1298
        foreach ($this->cols() as $col => $param) {
1299
            if (isset($this->_params[$col]))
1300
                $values[$col] = $this->_params[$col];
1301
        }
1302
        if (empty($values) && empty($options['empty'])) {
1303
            return false;
1304
        }
1305
1306
        if ($this->pk()) {
1307
            $new = false;
1308
            if ($this->get($this->_params[$this->index()])) {
1309
                App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1310
                App::$cur->db->update($this->table(), $values);
1311
            } else {
1312
1313
                $this->_params[$this->index()] = App::$cur->db->insert($this->table(), $values);
1314
            }
1315
        } else {
1316
            $new = true;
1317
            $this->_params[$this->index()] = App::$cur->db->insert($this->table(), $values);
1318
        }
1319
        App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1320
        try {
1321
            $result = App::$cur->db->select($this->table());
1322
        } catch (PDOException $exc) {
1323
            if ($exc->getCode() == '42S02') {
1324
                $this->createTable();
1325
            }
1326
            $result = App::$cur->db->select($this->table());
1327
        }
1328
        $this->_params = $result->fetch();
1329
        if ($new) {
1330
            Inji::$inst->event('modelCreatedItem-' . get_called_class(), $this);
1331
        }
1332
        $this->afterSave();
1333
        return $this->{$this->index()};
1334
    }
1335
1336
    /**
1337
     * After save trigger
1338
     */
1339
    public function afterSave()
1340
    {
1341
        
1342
    }
1343
1344
    /**
1345
     * Before delete trigger
1346
     */
1347
    public function beforeDelete()
1348
    {
1349
        
1350
    }
1351
1352
    /**
1353
     * Delete item from module storage
1354
     * 
1355
     * @param array $options
1356
     * @return boolean
1357
     */
1358
    public function deleteFromModuleStorage($options)
1359
    {
1360
1361
        $col = static::index();
1362
        $id = $this->pk();
1363
        $appType = '';
1364
        $classPath = explode('\\', get_called_class());
1365 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1366
            $moduleConfig = Config::share($classPath[0]);
1367
        } else {
1368
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
1369
        }
1370
1371 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1372
            if (empty($options['appType'])) {
1373
                $appType = App::$cur->type;
1374
            } else {
1375
                $appType = $options['appType'];
1376
            }
1377
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1378
        } else {
1379
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1380
        }
1381
        if (empty($storage[$classPath[1]])) {
1382
            $storage[$classPath[1]] = [];
1383
        }
1384 View Code Duplication
        foreach ($storage[$classPath[1]] as $key => $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1385
1386
            if ($item[$col] == $id) {
1387
                unset($storage[$classPath[1]][$key]);
1388
                break;
1389
            }
1390
        }
1391 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1392
            $moduleConfig['storage'][$appType] = $storage;
1393
        } else {
1394
            $moduleConfig['storage'] = $storage;
1395
        }
1396 View Code Duplication
        if (empty(static::$storage['options']['share'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1397
            Config::save('module', $moduleConfig, $classPath[0]);
1398
        } else {
1399
            Config::save('share', $moduleConfig, $classPath[0]);
1400
        }
1401
        return true;
1402
    }
1403
1404
    /**
1405
     * Delete item from data base
1406
     * 
1407
     * @param array $options
1408
     * @return boolean
1409
     */
1410
    public function delete($options = [])
1411
    {
1412
        $this->beforeDelete();
1413
1414
        if (static::$storage['type'] == 'moduleConfig') {
1415
            return static::deleteFromModuleStorage($options);
1416
        }
1417
        if (!empty($this->_params[$this->index()])) {
1418
            App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1419
            $result = App::$cur->db->delete($this->table());
1420
            if ($result) {
1421
                $this->afterDelete();
1422
                return $result;
1423
            }
1424
        }
1425
        return false;
1426
    }
1427
1428
    /**
1429
     * Delete items from data base
1430
     * 
1431
     * @param array $where
1432
     */
1433
    public static function deleteList($where)
1434
    {
1435
        if (!empty($where)) {
1436
            static::fixPrefix($where, 'first');
1437
            App::$cur->db->where($where);
1438
        }
1439
        App::$cur->db->delete(static::table());
1440
    }
1441
1442
    /**
1443
     * After delete trigger
1444
     */
1445
    public function afterDelete()
1446
    {
1447
        
1448
    }
1449
1450
    /**
1451
     * find relation for col name
1452
     * 
1453
     * @param string $col
1454
     * @return array|null
1455
     */
1456
    public static function findRelation($col)
1457
    {
1458
1459
        foreach (static::relations() as $relName => $rel) {
1460
            if ($rel['col'] == $col)
1461
                return $relName;
1462
        }
1463
        return NULL;
1464
    }
1465
1466
    /**
1467
     * Set params for model
1468
     * 
1469
     * @param array $params
1470
     */
1471
    public function setParams($params)
1472
    {
1473
        static::fixPrefix($params);
1474
        $className = get_called_class();
1475
        foreach ($params as $paramName => $value) {
1476
            $shortName = preg_replace('!' . $this->colPrefix() . '!', '', $paramName);
1477
            if (!empty($className::$cols[$shortName])) {
1478
                switch ($className::$cols[$shortName]['type']) {
1479
                    case 'decimal':
1480
                        $params[$paramName] = (float) $value;
1481
                        break;
1482
                    case 'number':
1483
                        $params[$paramName] = (int) $value;
1484
                        break;
1485
                    case 'bool':
1486
                        $params[$paramName] = (bool) $value;
1487
                        break;
1488
                }
1489
            }
1490
        }
1491
        $this->_params = array_merge($this->_params, $params);
1492
    }
1493
1494
    /**
1495
     * Return relation
1496
     * 
1497
     * @param string $relName
1498
     * @return array|boolean
1499
     */
1500
    public static function getRelation($relName)
1501
    {
1502
        $relations = static::relations();
1503
        return !empty($relations[$relName]) ? $relations[$relName] : false;
1504
    }
1505
1506
    /**
1507
     * Load relation
1508
     * 
1509
     * @param string $name
1510
     * @param array $params
1511
     * @return null|array|integer|\Model
1512
     */
1513
    public function loadRelation($name, $params = [])
1514
    {
1515
        $relation = static::getRelation($name);
1516
        if ($relation) {
1517
            if (!isset($relation['type'])) {
1518
                $type = 'to';
1519
            } else {
1520
                $type = $relation['type'];
1521
            }
1522
            $getCol = null;
1523
            $getParams = [];
1524
            switch ($type) {
1525
                case 'relModel':
1526
                    if (!$this->pk()) {
1527
                        return [];
1528
                    }
1529
                    $fixedCol = $relation['model']::index();
1530
                    $relation['relModel']::fixPrefix($fixedCol);
1531
                    $ids = array_keys($relation['relModel']::getList(['where' => [$this->index(), $this->pk()], 'array' => true, 'key' => $fixedCol]));
1532
                    if (empty($ids)) {
1533
                        if (empty($params['count'])) {
1534
                            return [];
1535
                        } else {
1536
                            return 0;
1537
                        }
1538
                    }
1539
                    $getType = 'getList';
1540
                    $options = [
1541
                        'where' => [$relation['model']::index(), implode(',', $ids), 'IN'],
1542
                        'array' => (!empty($params['array'])) ? true : false,
1543
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1544
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1545
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1546
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1547
                    ];
1548
                    break;
1549
                case 'many':
1550
                    if (!$this->{$this->index()}) {
1551
                        return [];
1552
                    }
1553
                    $getType = 'getList';
1554
                    $options = [
1555
                        'join' => (isset($relation['join'])) ? $relation['join'] : null,
1556
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1557
                        'array' => (!empty($params['array'])) ? true : false,
1558
                        'forSelect' => (!empty($params['forSelect'])) ? true : false,
1559
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1560
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1561
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1562
                        'appType' => (isset($params['appType'])) ? $params['appType'] : ((isset($relation['appType'])) ? $relation['appType'] : null),
1563
                        'where' => []
1564
                    ];
1565
                    $options['where'][] = [$relation['col'], $this->{$this->index()}];
1566
                    if (!empty($relation['where'])) {
1567
                        $options['where'] = array_merge($options['where'], [$relation['where']]);
1568
                    }
1569
                    if (!empty($params['where'])) {
1570
                        $options['where'] = array_merge($options['where'], [$params['where']]);
1571
                    }
1572
                    break;
1573
                case 'one':
1574
                    $getType = 'get';
1575
                    $options = [$relation['col'], $this->pk()];
1576
                    break;
1577
                default:
1578
                    if ($this->$relation['col'] === NULL) {
1579
                        return null;
1580
                    }
1581
                    $getType = 'get';
1582
                    $options = $this->$relation['col'];
1583
                    $getParams['appType'] = $this->appType;
1584
            }
1585
            if (!empty($params['count'])) {
1586
                if (class_exists($relation['model'])) {
1587
                    return $relation['model']::getCount($options);
1588
                }
1589
                return 0;
1590
            } else {
1591
                if (class_exists($relation['model'])) {
1592
                    $this->loadedRelations[$name][json_encode($params)] = $relation['model']::$getType($options, $getCol, $getParams);
1593
                } else {
1594
                    $this->loadedRelations[$name][json_encode($params)] = [];
1595
                }
1596
            }
1597
            return $this->loadedRelations[$name][json_encode($params)];
1598
        }
1599
        return NULL;
1600
    }
1601
1602
    /**
1603
     * Add relation item
1604
     * 
1605
     * @param string $relName
1606
     * @param \Model $objectId
1607
     * @return boolean
1608
     */
1609
    public function addRelation($relName, $objectId)
1610
    {
1611
        $relations = $this->relations();
1612
        if (isset($relations[$relName])) {
1613
            $relation = $relations[$relName];
1614
            App::$cur->db->where($relation['relTablePrefix'] . $this->index(), $this->pk());
1615
            App::$cur->db->where($relation['relTablePrefix'] . $relation['model']::index(), $objectId);
1616
            $isset = App::$cur->db->select($relation['relTable'])->fetch_assoc();
1617
            if ($isset)
1618
                return true;
1619
1620
            App::$cur->db->insert($relation['relTable'], [
1621
                $relation['relTablePrefix'] . $this->index() => $this->{$this->index()},
1622
                $relation['relTablePrefix'] . $relation['model']::index() => $objectId
1623
            ]);
1624
            return true;
1625
        }
1626
        return false;
1627
    }
1628
1629
    /**
1630
     * Check user access for form
1631
     * 
1632
     * @param string $formName
1633
     * @return boolean
1634
     */
1635
    public function checkFormAccess($formName)
1636
    {
1637
        if ($formName == 'manage' && !Users\User::$cur->isAdmin()) {
1638
            return false;
1639
        }
1640
        return true;
1641
    }
1642
1643
    /**
1644
     * Check access for model
1645
     * 
1646
     * @param string $mode
1647
     * @param \Users\User $user
1648
     * @return boolean
1649
     */
1650
    public function checkAccess($mode = 'write', $user = null)
1651
    {
1652
        if (!$user) {
1653
            $user = \Users\User::$cur;
1654
        }
1655
        return $user->isAdmin();
1656
    }
1657
1658
    /**
1659
     * Param and relation with params getter
1660
     * 
1661
     * @param string $name
1662
     * @param array $params
1663
     * @return \Value|mixed
1664
     */
1665
    public function __call($name, $params)
1666
    {
1667
        $fixedName = $name;
1668
        static::fixPrefix($fixedName);
1669 View Code Duplication
        if (isset($this->_params[$fixedName])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1670
            return new Value($this, $fixedName);
1671
        } elseif (isset($this->_params[$name])) {
1672
            return new Value($this, $name);
1673
        }
1674
        return call_user_func_array([$this, 'loadRelation'], array_merge([$name], $params));
1675
    }
1676
1677
    /**
1678
     * Param and relation getter
1679
     * 
1680
     * @param string $name
1681
     * @return mixed
1682
     */
1683
    public function __get($name)
1684
    {
1685
        $fixedName = $name;
1686
        static::fixPrefix($fixedName);
1687
        if (isset($this->_params[$fixedName])) {
1688
            return $this->_params[$fixedName];
1689
        }
1690
        if (isset($this->loadedRelations[$name][json_encode([])])) {
1691
            return $this->loadedRelations[$name][json_encode([])];
1692
        }
1693
        return $this->loadRelation($name);
1694
    }
1695
1696
    /**
1697
     * Return model value in object
1698
     * 
1699
     * @param string $name
1700
     * @return \Value|null
1701
     */
1702
    public function value($name)
1703
    {
1704
        $fixedName = $name;
1705
        static::fixPrefix($fixedName);
1706 View Code Duplication
        if (isset($this->_params[$fixedName])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1707
            return new Value($this, $fixedName);
1708
        } elseif ($this->_params[$name]) {
1709
            return new Value($this, $name);
1710
        }
1711
        return null;
1712
    }
1713
1714
    /**
1715
     * Return manager filters
1716
     * 
1717
     * @return array
1718
     */
1719
    public static function managerFilters()
1720
    {
1721
        return [];
1722
    }
1723
1724
    /**
1725
     * Return validators for cols
1726
     * 
1727
     * @return array
1728
     */
1729
    public static function validators()
1730
    {
1731
        return [];
1732
    }
1733
1734
    /**
1735
     * Return validator by name
1736
     * 
1737
     * @param string $name
1738
     * @return array
1739
     */
1740
    public static function validator($name)
1741
    {
1742
        $validators = static::validators();
1743
        if (!empty($validators[$name])) {
1744
            return $validators[$name];
1745
        }
1746
        return [];
1747
    }
1748
1749
    /**
1750
     * Set handler for model params
1751
     * 
1752
     * @param string $name
1753
     * @param mixed $value
1754
     */
1755
    public function __set($name, $value)
1756
    {
1757
        static::fixPrefix($name);
1758
        $className = get_called_class();
1759
        $shortName = preg_replace('!' . $this->colPrefix() . '!', '', $name);
1760
        if (!empty($className::$cols[$shortName])) {
1761
            switch ($className::$cols[$shortName]['type']) {
1762
                case 'decimal':
1763
                    $value = (float) $value;
1764
                    break;
1765
                case 'number':
1766
                    $value = (int) $value;
1767
                    break;
1768
                case 'bool':
1769
                    $value = (bool) $value;
1770
                    break;
1771
            }
1772
        }
1773
        if ((isset($this->_params[$name]) && $this->_params[$name] != $value) && !isset($this->_changedParams[$name])) {
1774
            $this->_changedParams[$name] = $this->_params[$name];
1775
        }
1776
1777
        $this->_params[$name] = $value;
1778
    }
1779
1780
    /**
1781
     * Isset handler for model params
1782
     * 
1783
     * @param string $name
1784
     * @return boolean
1785
     */
1786
    public function __isset($name)
1787
    {
1788
        static::fixPrefix($name);
1789
        return isset($this->_params[$name]);
1790
    }
1791
1792
    /**
1793
     * Convert object to string
1794
     * 
1795
     * @return string
1796
     */
1797
    public function __toString()
1798
    {
1799
        return $this->name();
1800
    }
1801
1802
}
1803