Completed
Push — master ( 55a9f3...13acd2 )
by Alexey
05:06
created

system/Inji/Model.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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':
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':
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])) {
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])) {
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]])) {
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':
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':
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':
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':
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']])) {
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) {
423
            Model::$cols[static::table()] = App::$cur->db->getTableCols(static::table());
424
        }
425 View Code Duplication
        if (!Model::$cols[static::table()]) {
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
     * Generate params string for col by name
434
     * 
435
     * @param string $colName
436
     * @return boolean|string
437
     */
438
    public static function genColParams($colName)
439
    {
440
        if (empty(static::$cols[$colName]) || static::$storage['type'] == 'moduleConfig') {
441
            return false;
442
        }
443
444
        $params = false;
445
        switch (static::$cols[$colName]['type']) {
446
            case 'select':
447
                switch (static::$cols[$colName]['source']) {
448
                    case 'relation':
449
                        $params = 'int(11) UNSIGNED NOT NULL';
450
                        break;
451
                    default:
452
                        $params = 'varchar(255) NOT NULL';
453
                }
454
                break;
455
            case 'image':
456
                $params = 'int(11) UNSIGNED NOT NULL';
457
                break;
458
            case 'number':
459
                $params = 'int(11) NOT NULL';
460
                break;
461
            case 'text':
462
            case 'email':
463
                $params = 'varchar(255) NOT NULL';
464
                break;
465
            case 'html':
466
            case 'textarea':
467
            case 'json':
468
            case 'password':
469
                $params = 'text NOT NULL';
470
                break;
471
            case 'bool':
472
                $params = 'tinyint(1) UNSIGNED NOT NULL';
473
                break;
474
            case 'decimal':
475
                $params = 'decimal(8, 2) NOT NULL';
476
                break;
477
            case 'date':
478
                $params = 'date NOT NULL DEFAULT 0';
479
                break;
480
            case 'dateTime':
481
                $params = 'timestamp NOT NULL DEFAULT 0';
482
                break;
483
        }
484
        return $params;
485
    }
486
487
    /**
488
     * Create new col in data base
489
     * 
490
     * @param string $colName
491
     * @return boolean|integer
492
     */
493
    public static function createCol($colName)
494
    {
495
        $params = static::genColParams($colName);
496
        if ($params === false) {
497
            return false;
498
        }
499
        return App::$cur->db->addCol(static::table(), static::colPrefix() . $colName, $params);
500
    }
501
502
    public static function createTable()
503
    {
504
        $query = App::$cur->db->newQuery();
505
        if (!$query) {
506
            return false;
507
        }
508
509
        if (!isset($this)) {
510
            $tableName = static::table();
511
            $colPrefix = static::colPrefix();
512
        } else {
513
            $tableName = $this->table();
514
            $colPrefix = $this->colPrefix();
515
        }
516
517
        $cols = [
518
            $colPrefix . 'id' => 'pk'
519
        ];
520
        $className = get_called_class();
521
        if (!empty($className::$cols)) {
522
            foreach ($className::$cols as $colName => $colParams) {
523
                if ($colName == 'date_create') {
524
                    $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
525
                    continue;
526
                }
527
                $params = $className::genColParams($colName);
528
                if ($params) {
529
                    $cols[$colPrefix . $colName] = $params;
530
                }
531
            }
532
        }
533
        if (empty($cols[$colPrefix . 'date_create'])) {
534
            $cols[$colPrefix . 'date_create'] = 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP';
535
        }
536
        $query->createTable($tableName, $cols);
537
        return true;
538
    }
539
540
    /**
541
     * Return table name
542
     * 
543
     * @return string
544
     */
545
    public static function table()
546
    {
547
        return strtolower(str_replace('\\', '_', get_called_class()));
548
    }
549
550
    /**
551
     * Return table index col name
552
     * 
553
     * @return string
554
     */
555
    public static function index()
556
    {
557
558
        return static::colPrefix() . 'id';
559
    }
560
561
    /**
562
     * Return col prefix
563
     * 
564
     * @return string
565
     */
566
    public static function colPrefix()
567
    {
568
        $classPath = explode('\\', get_called_class());
569
        $classPath = array_slice($classPath, 1);
570
        return strtolower(implode('_', $classPath)) . '_';
571
    }
572
573
    /**
574
     * return relations list
575
     * 
576
     * @return array
577
     */
578
    public static function relations()
579
    {
580
        return [];
581
    }
582
583
    /**
584
     * Return name of col with object name
585
     * 
586
     * @return string
587
     */
588
    public static function nameCol()
589
    {
590
        return 'name';
591
    }
592
593
    /**
594
     * Return object name
595
     * 
596
     * @return string
597
     */
598
    public function name()
599
    {
600
        return $this->{$this->nameCol()} ? $this->{$this->nameCol()} : $this->pk();
601
    }
602
603
    /**
604
     * Get single object from data base
605
     * 
606
     * @param mixed $param
607
     * @param string $col
608
     * @param array $options
609
     * @return boolean|\Model
610
     */
611
    public static function get($param, $col = null, $options = [])
612
    {
613
        if (static::$storage['type'] == 'moduleConfig') {
614
            return static::getFromModuleStorage($param, $col, $options);
615
        }
616
        if (!empty($col)) {
617
            static::fixPrefix($col);
618
        }
619
620
        if (is_array($param)) {
621
            static::fixPrefix($param, 'first');
622
        }
623
        foreach (static::$relJoins as $join) {
624
            App::$cur->db->join($join[0], $join[1]);
625
        }
626
        static::$relJoins = [];
627 View Code Duplication
        foreach (static::$needJoin as $rel) {
628
            $relations = static::relations();
629
            if (isset($relations[$rel])) {
630
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
631
                switch ($type) {
632
                    case 'to':
633
                        $relCol = $relations[$rel]['col'];
634
                        static::fixPrefix($relCol);
635
                        App::$cur->db->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
636
                        break;
637
                    case 'one':
638
                        $col = $relations[$rel]['col'];
639
                        $relations[$rel]['model']::fixPrefix($col);
640
                        App::$cur->db->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
641
                        break;
642
                }
643
            }
644
        }
645
        static::$needJoin = [];
646
        if (is_array($param)) {
647
            App::$cur->db->where($param);
648
        } else {
649
            if ($col === null) {
650
651
                $col = static::index();
652
            }
653
            if ($param !== null) {
654
                $cols = static::cols();
655
                if (!isset($cols[$col]) && isset($cols[static::colPrefix() . $col])) {
656
                    $col = static::colPrefix() . $col;
657
                }
658
                App::$cur->db->where($col, $param);
659
            } else {
660
                return false;
661
            }
662
        }
663
        if (!App::$cur->db->where) {
664
            return false;
665
        }
666
        try {
667
            $result = App::$cur->db->select(static::table());
668
        } catch (PDOException $exc) {
669
            if ($exc->getCode() == '42S02') {
670
                static::createTable();
671
            }
672
            $result = App::$cur->db->select(static::table());
673
        }
674
        if (!$result) {
675
            return false;
676
        }
677
        return $result->fetch(get_called_class());
678
    }
679
680
    /**
681
     * Old method
682
     * 
683
     * @param type $options
684
     * @return Array
685
     */
686
    public static function get_list($options = [])
687
    {
688
        if (!empty($options['where']))
689
            App::$cur->db->where($options['where']);
690
        if (!empty($options['group'])) {
691
            App::$cur->db->group($options['group']);
692
        }
693
        if (!empty($options['order']))
694
            App::$cur->db->order($options['order']);
695
        if (!empty($options['join']))
696
            App::$cur->db->join($options['join']);
697
        if (!empty($options['distinct']))
698
            App::$cur->db->distinct = $options['distinct'];
699
700
        foreach (static::$relJoins as $join) {
701
            App::$cur->db->join($join[0], $join[1]);
702
        }
703
        static::$relJoins = [];
704 View Code Duplication
        foreach (static::$needJoin as $rel) {
705
            $relations = static::relations();
706
            if (isset($relations[$rel])) {
707
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
708
                switch ($type) {
709
                    case 'to':
710
                        $relCol = $relations[$rel]['col'];
711
                        static::fixPrefix($relCol);
712
                        App::$cur->db->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
713
                        break;
714
                    case 'one':
715
                        $col = $relations[$rel]['col'];
716
                        $relations[$rel]['model']::fixPrefix($col);
717
                        App::$cur->db->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
718
                        break;
719
                }
720
            }
721
        }
722
        static::$needJoin = [];
723
724 View Code Duplication
        if (!empty($options['limit']))
725
            $limit = (int) $options['limit'];
726
        else {
727
            $limit = 0;
728
        }
729 View Code Duplication
        if (!empty($options['start']))
730
            $start = (int) $options['start'];
731
        else {
732
            $start = 0;
733
        }
734
        if ($limit || $start) {
735
            App::$cur->db->limit($start, $limit);
736
        }
737
        if (isset($options['key'])) {
738
            $key = $options['key'];
739
        } else {
740
            $key = static::index();
741
        }
742
        try {
743
            $result = App::$cur->db->select(static::table());
744
        } catch (PDOException $exc) {
745
            if ($exc->getCode() == '42S02') {
746
                static::createTable();
747
            }
748
            $result = App::$cur->db->select(static::table());
749
        }
750
751
        if (!empty($options['array'])) {
752
            return $result->getArray($key);
753
        }
754
        $list = $result->getObjects(get_called_class(), $key);
755 View Code Duplication
        if (!empty($options['forSelect'])) {
756
            $return = [];
757
            foreach ($list as $key => $item) {
758
                $return[$key] = $item->name();
759
            }
760
            return $return;
761
        }
762
        return $list;
763
    }
764
765
    /**
766
     * Return list of objects from data base
767
     * 
768
     * @param type $options
769
     * @return type
770
     */
771
    public static function getList($options = [])
772
    {
773
        if (static::$storage['type'] != 'db') {
774
            return static::getListFromModuleStorage($options);
775
        }
776
        if (!empty($options['where'])) {
777
            static::fixPrefix($options['where'], 'first');
778
        }
779
        if (!empty($options['order'])) {
780
            static::fixPrefix($options['order'], 'first');
781
        }
782
        return static::get_list($options);
783
    }
784
785
    /**
786
     * Get single item from module storage
787
     * 
788
     * @param array $param
789
     * @param string $col
790
     * @param array $options
791
     * @return boolean|\Model
792
     */
793
    public static function getFromModuleStorage($param = null, $col = null, $options = [])
794
    {
795
        if ($col === null) {
796
797
            $col = static::index();
798
        }
799
        if ($param == null) {
800
            return false;
801
        }
802
        $classPath = explode('\\', get_called_class());
803 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
804
            $moduleConfig = Config::share($classPath[0]);
805
        } else {
806
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
807
        }
808
        $appType = App::$cur->type;
809 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
810
            if (!empty($options['appType'])) {
811
                $appType = $options['appType'];
812
            }
813
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
814
        } else {
815
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
816
        }
817
        if (!empty($storage[$classPath[1]])) {
818
            $items = $storage[$classPath[1]];
819
            $class = get_called_class();
820
            foreach ($items as $key => $item) {
821
                if ($item[$col] == $param) {
822
                    if (!empty($options['array'])) {
823
                        return $item;
824
                    }
825
                    $item = new $class($item);
826
                    $item->appType = $appType;
827
                    return $item;
828
                }
829
            }
830
        }
831
        return false;
832
    }
833
834
    /**
835
     * Return list items from module storage
836
     * 
837
     * @param array $options
838
     * @return array
839
     */
840
    public static function getListFromModuleStorage($options = [])
841
    {
842
        $classPath = explode('\\', get_called_class());
843 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
844
            $moduleConfig = Config::share($classPath[0]);
845
        } else {
846
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
847
        }
848 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
849
            if (empty($options['appType'])) {
850
                $appType = App::$cur->type;
851
            } else {
852
                $appType = $options['appType'];
853
            }
854
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
855
        } else {
856
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
857
        }
858
        if (!empty($storage[$classPath[1]])) {
859
            $items = [];
860
            $class = get_called_class();
861
            if (isset($options['key'])) {
862
                $arrayKey = $options['key'];
863
            } else {
864
                $arrayKey = static::index();
865
            }
866
            foreach ($storage[$classPath[1]] as $key => $item) {
867
                if (!empty($options['where']) && !Model::checkWhere($item, $options['where'])) {
868
                    continue;
869
                }
870
                $items[$item[$arrayKey]] = new $class($item);
871
            }
872
            if (!empty($options['order'])) {
873
                usort($items, function($a, $b) use($options) {
874
                    if ($a->{$options['order'][0]} > $b->{$options['order'][0]} && $options['order'][1] = 'asc') {
875
                        return 1;
876
                    } elseif ($a->{$options['order'][0]} < $b->{$options['order'][0]} && $options['order'][1] = 'asc') {
877
                        return -1;
878
                    }
879
                    return 0;
880
                });
881
            }
882 View Code Duplication
            if (!empty($options['forSelect'])) {
883
                $return = [];
884
                foreach ($items as $key => $item) {
885
                    $return[$key] = $item->name();
886
                }
887
                return $return;
888
            }
889
            return $items;
890
        }
891
        return [];
892
    }
893
894
    /**
895
     * Return count of records from module storage
896
     * 
897
     * @param array $options
898
     * @return int
899
     */
900
    public static function getCountFromModuleStorage($options = [])
901
    {
902
903
        $classPath = explode('\\', get_called_class());
904
        $count = 0;
905
        if (empty($options['appType'])) {
906
            $appType = App::$cur->type;
907
        } else {
908
            $appType = $options['appType'];
909
        }
910 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
911
            $moduleConfig = Config::share($classPath[0]);
912
        } else {
913
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
914
        }
915
        if (!empty($moduleConfig['storage'][$appType][$classPath[1]])) {
916
            $items = $moduleConfig['storage'][$appType][$classPath[1]];
917
            if (empty($options['where'])) {
918
                return count($items);
919
            }
920
            foreach ($items as $key => $item) {
921
                if (!empty($options['where'])) {
922
                    if (Model::checkWhere($item, $options['where'])) {
923
                        $count++;
924
                    }
925
                } else {
926
                    $count++;
927
                }
928
            }
929
        }
930
        return $count;
931
    }
932
933
    /**
934
     * Check where for module storage query
935
     * 
936
     * @param array $item
937
     * @param array|string $where
938
     * @param string $value
939
     * @param string $operation
940
     * @param string $concatenation
941
     * @return boolean
942
     */
943
    public static function checkWhere($item = [], $where = '', $value = '', $operation = '=', $concatenation = 'AND')
944
    {
945
946
        if (is_array($where)) {
947
            if (is_array($where[0])) {
948
                foreach ($where as $whereItem) {
949
                    $result = forward_static_call_array(['Model', 'checkWhere'], array_merge([$item], $whereItem));
950
                    if (!$result) {
951
                        return false;
952
                    }
953
                }
954
                return true;
955
            } else {
956
                return forward_static_call_array(['Model', 'checkWhere'], array_merge([$item], $where));
957
            }
958
        }
959
960
        if (!isset($item[$where]) && !$value) {
961
            return true;
962
        }
963
        if (!isset($item[$where]) && $value) {
964
            return false;
965
        }
966
        if ($item[$where] == $value) {
967
            return true;
968
        }
969
        return false;
970
    }
971
972
    /**
973
     * Return count of records from data base
974
     * 
975
     * @param array $options
976
     * @return array|int
977
     */
978
    public static function getCount($options = [])
979
    {
980
        if (static::$storage['type'] == 'moduleConfig') {
981
            return static::getCountFromModuleStorage($options);
982
        }
983
        if (!empty($options['where'])) {
984
            static::fixPrefix($options['where'], 'first');
985
        }
986
        if (!empty($options['where']))
987
            App::$cur->db->where($options['where']);
988
        if (!empty($options['join']))
989
            App::$cur->db->join($options['join']);
990
        if (!empty($options['order'])) {
991
            App::$cur->db->order($options['order']);
992
        }
993 View Code Duplication
        if (!empty($options['limit']))
994
            $limit = (int) $options['limit'];
995
        else {
996
            $limit = 0;
997
        }
998 View Code Duplication
        if (!empty($options['start']))
999
            $start = (int) $options['start'];
1000
        else {
1001
            $start = 0;
1002
        }
1003
        if ($limit || $start) {
1004
            App::$cur->db->limit($start, $limit);
1005
        }
1006
1007
        foreach (static::$relJoins as $join) {
1008
            App::$cur->db->join($join[0], $join[1]);
1009
        }
1010
        static::$relJoins = [];
1011 View Code Duplication
        foreach (static::$needJoin as $rel) {
1012
            $relations = static::relations();
1013
            if (isset($relations[$rel])) {
1014
                $type = empty($relations[$rel]['type']) ? 'to' : $relations[$rel]['type'];
1015
                switch ($type) {
1016
                    case 'to':
1017
                        $relCol = $relations[$rel]['col'];
1018
                        static::fixPrefix($relCol);
1019
                        App::$cur->db->join($relations[$rel]['model']::table(), $relations[$rel]['model']::index() . ' = ' . $relCol);
1020
                        break;
1021
                    case 'one':
1022
                        $col = $relations[$rel]['col'];
1023
                        $relations[$rel]['model']::fixPrefix($col);
1024
                        App::$cur->db->join($relations[$rel]['model']::table(), static::index() . ' = ' . $col);
1025
                        break;
1026
                }
1027
            }
1028
        }
1029
        static::$needJoin = [];
1030
        $cols = 'COUNT(';
1031
1032
        if (!empty($options['distinct'])) {
1033
            if (is_bool($options['distinct'])) {
1034
                $cols .= 'DISTINCT *';
1035
            } else {
1036
                $cols .= "DISTINCT {$options['distinct']}";
1037
            }
1038
        } else {
1039
            $cols .= '*';
1040
        }
1041
        $cols .=') as `count`' . (!empty($options['cols']) ? ',' . $options['cols'] : '');
1042
        App::$cur->db->cols = $cols;
1043
        if (!empty($options['group'])) {
1044
            App::$cur->db->group($options['group']);
1045
        }
1046
        try {
1047
            $result = App::$cur->db->select(static::table());
1048
        } catch (PDOException $exc) {
1049
            if ($exc->getCode() == '42S02') {
1050
                static::createTable();
1051
            }
1052
            $result = App::$cur->db->select(static::table());
1053
        }
1054
        if (!empty($options['group'])) {
1055
            $count = $result->getArray();
1056
            return $count;
1057
        } else {
1058
            $count = $result->fetch();
1059
            return $count['count'];
1060
        }
1061
    }
1062
1063
    /**
1064
     * Update records in data base
1065
     * 
1066
     * @param array $params
1067
     * @param array $where
1068
     * @return boolean
1069
     */
1070
    public static function update($params, $where = [])
1071
    {
1072
        static::fixPrefix($params);
1073
1074
        $cols = self::cols();
1075
1076
        $values = [];
1077
        foreach ($cols as $col => $param) {
1078
            if (isset($params[$col]))
1079
                $values[$col] = $params[$col];
1080
        }
1081
        if (empty($values)) {
1082
            return false;
1083
        }
1084
1085
        if (!empty($where)) {
1086
            static::fixPrefix($where, 'key');
1087
1088
            App::$cur->db->where($where);
1089
        }
1090
        App::$cur->db->update(static::table(), $values);
1091
    }
1092
1093
    /**
1094
     * Return primary key of object
1095
     * 
1096
     * @return mixed
1097
     */
1098
    public function pk()
1099
    {
1100
        return $this->{$this->index()};
1101
    }
1102
1103
    /**
1104
     * Before save trigger
1105
     */
1106
    public function beforeSave()
1107
    {
1108
        
1109
    }
1110
1111
    /**
1112
     * Save object to module storage
1113
     * 
1114
     * @param array $options
1115
     * @return boolean
1116
     */
1117
    public function saveModuleStorage($options)
1118
    {
1119
1120
        $col = static::index();
1121
        $id = $this->pk();
1122
        $appType = '';
1123
        $classPath = explode('\\', get_called_class());
1124
1125 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
1126
            $moduleConfig = Config::share($classPath[0]);
1127
        } else {
1128
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
1129
        }
1130
1131 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1132
            if (empty($options['appType'])) {
1133
                $appType = App::$cur->type;
1134
            } else {
1135
                $appType = $options['appType'];
1136
            }
1137
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1138
        } else {
1139
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1140
        }
1141
        if (empty($storage[$classPath[1]])) {
1142
            $storage[$classPath[1]] = [];
1143
        }
1144
        if ($id) {
1145 View Code Duplication
            foreach ($storage[$classPath[1]] as $key => $item) {
1146
                if ($item[$col] == $id) {
1147
                    $storage[$classPath[1]][$key] = $this->_params;
1148
                    break;
1149
                }
1150
            }
1151
        } else {
1152
            $id = !empty($storage['scheme'][$classPath[1]]['ai']) ? $storage['scheme'][$classPath[1]]['ai'] : 1;
1153
            $this->$col = $id;
1154
            $storage['scheme'][$classPath[1]]['ai'] = $id + 1;
1155
            $storage[$classPath[1]][] = $this->_params;
1156
        }
1157 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1158
            $moduleConfig['storage'][$appType] = $storage;
1159
        } else {
1160
            $moduleConfig['storage'] = $storage;
1161
        }
1162 View Code Duplication
        if (empty(static::$storage['options']['share'])) {
1163
            Config::save('module', $moduleConfig, $classPath[0]);
1164
        } else {
1165
            Config::save('share', $moduleConfig, $classPath[0]);
1166
        }
1167
        return true;
1168
    }
1169
1170
    /**
1171
     * Update tree path category
1172
     */
1173
    public function changeCategoryTree()
1174
    {
1175
        $class = get_class($this);
1176
        $itemModel = $class::$treeCategory;
1177
        $oldPath = $this->tree_path;
1178
        $this->tree_path = $this->getCatalogTree($this);
1179
        $itemsTable = \App::$cur->db->table_prefix . $itemModel::table();
1180
        $itemTreeCol = $itemModel::colPrefix() . 'tree_path';
1181
1182
        $categoryTreeCol = $this->colPrefix() . 'tree_path';
1183
        $categoryTable = \App::$cur->db->table_prefix . $this->table();
1184
        if ($oldPath) {
1185
            \App::$cur->db->query('UPDATE
1186
                ' . $categoryTable . ' 
1187
                    SET 
1188
                        ' . $categoryTreeCol . ' = REPLACE(' . $categoryTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $this->tree_path . $this->id . '/' . '") 
1189
                    WHERE ' . $categoryTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1190
1191
            \App::$cur->db->query('UPDATE
1192
                ' . $itemsTable . '
1193
                    SET 
1194
                        ' . $itemTreeCol . ' = REPLACE(' . $itemTreeCol . ', "' . $oldPath . $this->id . '/' . '", "' . $this->tree_path . $this->id . '/' . '") 
1195
                    WHERE ' . $itemTreeCol . ' LIKE "' . $oldPath . $this->id . '/' . '%"');
1196
        }
1197
        $itemModel::update([$itemTreeCol => $this->tree_path . $this->id . '/'], [$itemModel::colPrefix() . $this->index(), $this->id]);
1198
    }
1199
1200
    /**
1201
     * Return tree path
1202
     * 
1203
     * @param \Model $catalog
1204
     * @return string
1205
     */
1206
    public function getCatalogTree($catalog)
1207
    {
1208
        $catalogClass = get_class($catalog);
1209
        $catalogParent = $catalogClass::get($catalog->parent_id);
1210
        if ($catalog && $catalogParent) {
1211
            if ($catalogParent->tree_path) {
1212
                return $catalogParent->tree_path . $catalogParent->id . '/';
1213
            } else {
1214
                return $this->getCatalogTree($catalogParent) . $catalogParent->id . '/';
1215
            }
1216
        }
1217
        return '/';
1218
    }
1219
1220
    /**
1221
     * Update tree path item
1222
     */
1223
    public function changeItemTree()
1224
    {
1225
        $class = get_class($this);
1226
        $categoryModel = $class::$categoryModel;
1227
        $category = $categoryModel::get($this->{$categoryModel::index()});
1228
        if ($category) {
1229
            $this->tree_path = $category->tree_path . $category->pk() . '/';
1230
        } else {
1231
            $this->tree_path = '/';
1232
        }
1233
    }
1234
1235
    /**
1236
     * Save object to data base
1237
     * 
1238
     * @param array $options
1239
     * @return boolean|int
1240
     */
1241
    public function save($options = [])
1242
    {
1243
1244
        if (static::$storage['type'] == 'moduleConfig') {
1245
            return static::saveModuleStorage($options);
1246
        }
1247
        $class = get_class($this);
1248
        if ($class::$categoryModel) {
1249
            $this->changeItemTree();
1250
        }
1251
        if ($class::$treeCategory) {
1252
            $this->changeCategoryTree();
1253
        }
1254
        if (!empty($this->_changedParams) && $this->pk()) {
1255
            Inji::$inst->event('modelItemParamsChanged-' . get_called_class(), $this);
1256
        }
1257
        $this->beforeSave();
1258
1259
        $values = [];
1260
1261
        foreach ($this->cols() as $col => $param) {
1262
            if (isset($this->_params[$col]))
1263
                $values[$col] = $this->_params[$col];
1264
        }
1265
        if (empty($values) && empty($options['empty'])) {
1266
            return false;
1267
        }
1268
1269
        if ($this->pk()) {
1270
            $new = false;
1271
            if ($this->get($this->_params[$this->index()])) {
1272
                App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1273
                App::$cur->db->update($this->table(), $values);
1274
            } else {
1275
1276
                $this->_params[$this->index()] = App::$cur->db->insert($this->table(), $values);
1277
            }
1278
        } else {
1279
            $new = true;
1280
            $this->_params[$this->index()] = App::$cur->db->insert($this->table(), $values);
1281
        }
1282
        App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1283
        try {
1284
            $result = App::$cur->db->select($this->table());
1285
        } catch (PDOException $exc) {
1286
            if ($exc->getCode() == '42S02') {
1287
                $this->createTable();
1288
            }
1289
            $result = App::$cur->db->select($this->table());
1290
        }
1291
        $this->_params = $result->fetch();
1292
        if ($new) {
1293
            Inji::$inst->event('modelCreatedItem-' . get_called_class(), $this);
1294
        }
1295
        $this->afterSave();
1296
        return $this->{$this->index()};
1297
    }
1298
1299
    /**
1300
     * After save trigger
1301
     */
1302
    public function afterSave()
1303
    {
1304
        
1305
    }
1306
1307
    /**
1308
     * Before delete trigger
1309
     */
1310
    public function beforeDelete()
1311
    {
1312
        
1313
    }
1314
1315
    /**
1316
     * Delete item from module storage
1317
     * 
1318
     * @param array $options
1319
     * @return boolean
1320
     */
1321
    public function deleteFromModuleStorage($options)
1322
    {
1323
1324
        $col = static::index();
1325
        $id = $this->pk();
1326
        $appType = '';
1327
        $classPath = explode('\\', get_called_class());
1328 View Code Duplication
        if (!empty(static::$storage['options']['share'])) {
1329
            $moduleConfig = Config::share($classPath[0]);
1330
        } else {
1331
            $moduleConfig = Config::module($classPath[0], strpos(static::$storage['type'], 'system') !== false);
1332
        }
1333
1334 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1335
            if (empty($options['appType'])) {
1336
                $appType = App::$cur->type;
1337
            } else {
1338
                $appType = $options['appType'];
1339
            }
1340
            $storage = !empty($moduleConfig['storage'][$appType]) ? $moduleConfig['storage'][$appType] : [];
1341
        } else {
1342
            $storage = !empty($moduleConfig['storage']) ? $moduleConfig['storage'] : [];
1343
        }
1344
        if (empty($storage[$classPath[1]])) {
1345
            $storage[$classPath[1]] = [];
1346
        }
1347 View Code Duplication
        foreach ($storage[$classPath[1]] as $key => $item) {
1348
1349
            if ($item[$col] == $id) {
1350
                unset($storage[$classPath[1]][$key]);
1351
                break;
1352
            }
1353
        }
1354 View Code Duplication
        if (!empty($moduleConfig['storage']['appTypeSplit'])) {
1355
            $moduleConfig['storage'][$appType] = $storage;
1356
        } else {
1357
            $moduleConfig['storage'] = $storage;
1358
        }
1359 View Code Duplication
        if (empty(static::$storage['options']['share'])) {
1360
            Config::save('module', $moduleConfig, $classPath[0]);
1361
        } else {
1362
            Config::save('share', $moduleConfig, $classPath[0]);
1363
        }
1364
        return true;
1365
    }
1366
1367
    /**
1368
     * Delete item from data base
1369
     * 
1370
     * @param array $options
1371
     * @return boolean
1372
     */
1373
    public function delete($options = [])
1374
    {
1375
        $this->beforeDelete();
1376
1377
        if (static::$storage['type'] == 'moduleConfig') {
1378
            return static::deleteFromModuleStorage($options);
1379
        }
1380
        if (!empty($this->_params[$this->index()])) {
1381
            App::$cur->db->where($this->index(), $this->_params[$this->index()]);
1382
            $result = App::$cur->db->delete($this->table());
1383
            if ($result) {
1384
                $this->afterDelete();
1385
                return $result;
1386
            }
1387
        }
1388
        return false;
1389
    }
1390
1391
    /**
1392
     * Delete items from data base
1393
     * 
1394
     * @param array $where
1395
     */
1396
    public static function deleteList($where)
1397
    {
1398
        if (!empty($where)) {
1399
            static::fixPrefix($where, 'first');
1400
            App::$cur->db->where($where);
1401
        }
1402
        App::$cur->db->delete(static::table());
1403
    }
1404
1405
    /**
1406
     * After delete trigger
1407
     */
1408
    public function afterDelete()
1409
    {
1410
        
1411
    }
1412
1413
    /**
1414
     * find relation for col name
1415
     * 
1416
     * @param string $col
1417
     * @return array|null
1418
     */
1419
    public static function findRelation($col)
1420
    {
1421
1422
        foreach (static::relations() as $relName => $rel) {
1423
            if ($rel['col'] == $col)
1424
                return $relName;
1425
        }
1426
        return NULL;
1427
    }
1428
1429
    /**
1430
     * Set params for model
1431
     * 
1432
     * @param array $params
1433
     */
1434
    public function setParams($params)
1435
    {
1436
        static::fixPrefix($params);
1437
        $className = get_called_class();
1438
        foreach ($params as $paramName => $value) {
1439
            $shortName = preg_replace('!' . $this->colPrefix() . '!', '', $paramName);
1440
            if (!empty($className::$cols[$shortName])) {
1441
                switch ($className::$cols[$shortName]['type']) {
1442
                    case 'decimal':
1443
                        $params[$paramName] = (float) $value;
1444
                        break;
1445
                    case 'number':
1446
                        $params[$paramName] = (int) $value;
1447
                        break;
1448
                    case 'bool':
1449
                        $params[$paramName] = (bool) $value;
1450
                        break;
1451
                }
1452
            }
1453
        }
1454
        $this->_params = array_merge($this->_params, $params);
1455
    }
1456
1457
    /**
1458
     * Return relation
1459
     * 
1460
     * @param string $relName
1461
     * @return array|boolean
1462
     */
1463
    public static function getRelation($relName)
1464
    {
1465
        $relations = static::relations();
1466
        return !empty($relations[$relName]) ? $relations[$relName] : false;
1467
    }
1468
1469
    /**
1470
     * Load relation
1471
     * 
1472
     * @param string $name
1473
     * @param array $params
1474
     * @return null|array|integer|\Model
1475
     */
1476
    public function loadRelation($name, $params = [])
1477
    {
1478
        $relation = static::getRelation($name);
1479
        if ($relation) {
1480
            if (!isset($relation['type'])) {
1481
                $type = 'to';
1482
            } else {
1483
                $type = $relation['type'];
1484
            }
1485
            $getCol = null;
1486
            $getParams = [];
1487
            switch ($type) {
1488
                case 'relModel':
1489
                    if (!$this->pk()) {
1490
                        return [];
1491
                    }
1492
                    $fixedCol = $relation['model']::index();
1493
                    $relation['relModel']::fixPrefix($fixedCol);
1494
                    $ids = array_keys($relation['relModel']::getList(['where' => [$this->index(), $this->pk()], 'array' => true, 'key' => $fixedCol]));
1495
                    if (empty($ids)) {
1496
                        if (empty($params['count'])) {
1497
                            return [];
1498
                        } else {
1499
                            return 0;
1500
                        }
1501
                    }
1502
                    $getType = 'getList';
1503
                    $options = [
1504
                        'where' => [$relation['model']::index(), implode(',', $ids), 'IN'],
1505
                        'array' => (!empty($params['array'])) ? true : false,
1506
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1507
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1508
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1509
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1510
                    ];
1511
                    break;
1512
                case 'many':
1513
                    if (!$this->{$this->index()}) {
1514
                        return [];
1515
                    }
1516
                    $getType = 'getList';
1517
                    $options = [
1518
                        'join' => (isset($relation['join'])) ? $relation['join'] : null,
1519
                        'key' => (isset($params['key'])) ? $params['key'] : ((isset($relation['resultKey'])) ? $relation['resultKey'] : null),
1520
                        'array' => (!empty($params['array'])) ? true : false,
1521
                        'forSelect' => (!empty($params['forSelect'])) ? true : false,
1522
                        'order' => (isset($params['order'])) ? $params['order'] : ((isset($relation['order'])) ? $relation['order'] : null),
1523
                        'start' => (isset($params['start'])) ? $params['start'] : ((isset($relation['start'])) ? $relation['start'] : null),
1524
                        'limit' => (isset($params['limit'])) ? $params['limit'] : ((isset($relation['limit'])) ? $relation['limit'] : null),
1525
                        'appType' => (isset($params['appType'])) ? $params['appType'] : ((isset($relation['appType'])) ? $relation['appType'] : null),
1526
                        'where' => []
1527
                    ];
1528
                    $options['where'][] = [$relation['col'], $this->{$this->index()}];
1529
                    if (!empty($relation['where'])) {
1530
                        $options['where'] = array_merge($options['where'], [$relation['where']]);
1531
                    }
1532
                    if (!empty($params['where'])) {
1533
                        $options['where'] = array_merge($options['where'], [$params['where']]);
1534
                    }
1535
                    break;
1536
                case 'one':
1537
                    $getType = 'get';
1538
                    $options = [$relation['col'], $this->pk()];
1539
                    break;
1540
                default:
1541
                    if ($this->$relation['col'] === NULL) {
1542
                        return null;
1543
                    }
1544
                    $getType = 'get';
1545
                    $options = $this->$relation['col'];
1546
                    $getParams['appType'] = $this->appType;
1547
            }
1548
            if (!empty($params['count'])) {
1549
                if (class_exists($relation['model'])) {
1550
                    return $relation['model']::getCount($options);
1551
                }
1552
                return 0;
1553
            } else {
1554
                if (class_exists($relation['model'])) {
1555
                    $this->loadedRelations[$name][json_encode($params)] = $relation['model']::$getType($options, $getCol, $getParams);
1556
                } else {
1557
                    $this->loadedRelations[$name][json_encode($params)] = [];
1558
                }
1559
            }
1560
            return $this->loadedRelations[$name][json_encode($params)];
1561
        }
1562
        return NULL;
1563
    }
1564
1565
    /**
1566
     * Add relation item
1567
     * 
1568
     * @param string $relName
1569
     * @param \Model $objectId
1570
     * @return boolean
1571
     */
1572
    public function addRelation($relName, $objectId)
1573
    {
1574
        $relations = $this->relations();
1575
        if (isset($relations[$relName])) {
1576
            $relation = $relations[$relName];
1577
            App::$cur->db->where($relation['relTablePrefix'] . $this->index(), $this->pk());
1578
            App::$cur->db->where($relation['relTablePrefix'] . $relation['model']::index(), $objectId);
1579
            $isset = App::$cur->db->select($relation['relTable'])->fetch_assoc();
1580
            if ($isset)
1581
                return true;
1582
1583
            App::$cur->db->insert($relation['relTable'], [
1584
                $relation['relTablePrefix'] . $this->index() => $this->{$this->index()},
1585
                $relation['relTablePrefix'] . $relation['model']::index() => $objectId
1586
            ]);
1587
            return true;
1588
        }
1589
        return false;
1590
    }
1591
1592
    /**
1593
     * Check user access for form
1594
     * 
1595
     * @param string $formName
1596
     * @return boolean
1597
     */
1598
    public function checkFormAccess($formName)
1599
    {
1600
        if ($formName == 'manage' && !Users\User::$cur->isAdmin()) {
1601
            return false;
1602
        }
1603
        return true;
1604
    }
1605
1606
    /**
1607
     * Check access for model
1608
     * 
1609
     * @param string $mode
1610
     * @param \Users\User $user
1611
     * @return boolean
1612
     */
1613
    public function checkAccess($mode = 'write', $user = null)
1614
    {
1615
        if (!$user) {
1616
            $user = \Users\User::$cur;
1617
        }
1618
        return $user->isAdmin();
1619
    }
1620
1621
    /**
1622
     * Param and relation with params getter
1623
     * 
1624
     * @param string $name
1625
     * @param array $params
1626
     * @return \Value|mixed
1627
     */
1628
    public function __call($name, $params)
1629
    {
1630
        $fixedName = $name;
1631
        static::fixPrefix($fixedName);
1632 View Code Duplication
        if (isset($this->_params[$fixedName])) {
1633
            return new Value($this, $fixedName);
1634
        } elseif (isset($this->_params[$name])) {
1635
            return new Value($this, $name);
1636
        }
1637
        return call_user_func_array([$this, 'loadRelation'], array_merge([$name], $params));
1638
    }
1639
1640
    /**
1641
     * Param and relation getter
1642
     * 
1643
     * @param string $name
1644
     * @return mixed
1645
     */
1646
    public function __get($name)
1647
    {
1648
        $fixedName = $name;
1649
        static::fixPrefix($fixedName);
1650
        if (isset($this->_params[$fixedName])) {
1651
            return $this->_params[$fixedName];
1652
        }
1653
        if (isset($this->loadedRelations[$name][json_encode([])])) {
1654
            return $this->loadedRelations[$name][json_encode([])];
1655
        }
1656
        return $this->loadRelation($name);
1657
    }
1658
1659
    /**
1660
     * Return model value in object
1661
     * 
1662
     * @param string $name
1663
     * @return \Value|null
1664
     */
1665
    public function value($name)
1666
    {
1667
        $fixedName = $name;
1668
        static::fixPrefix($fixedName);
1669 View Code Duplication
        if (isset($this->_params[$fixedName])) {
1670
            return new Value($this, $fixedName);
1671
        } elseif ($this->_params[$name]) {
1672
            return new Value($this, $name);
1673
        }
1674
        return null;
1675
    }
1676
1677
    /**
1678
     * Return manager filters
1679
     * 
1680
     * @return array
1681
     */
1682
    public static function managerFilters()
1683
    {
1684
        return [];
1685
    }
1686
1687
    /**
1688
     * Return validators for cols
1689
     * 
1690
     * @return array
1691
     */
1692
    public static function validators()
1693
    {
1694
        return [];
1695
    }
1696
1697
    /**
1698
     * Return validator by name
1699
     * 
1700
     * @param string $name
1701
     * @return array
1702
     */
1703
    public static function validator($name)
1704
    {
1705
        $validators = static::validators();
1706
        if (!empty($validators[$name])) {
1707
            return $validators[$name];
1708
        }
1709
        return [];
1710
    }
1711
1712
    /**
1713
     * Set handler for model params
1714
     * 
1715
     * @param string $name
1716
     * @param mixed $value
1717
     */
1718
    public function __set($name, $value)
1719
    {
1720
        static::fixPrefix($name);
1721
        $className = get_called_class();
1722
        $shortName = preg_replace('!' . $this->colPrefix() . '!', '', $name);
1723
        if (!empty($className::$cols[$shortName])) {
1724
            switch ($className::$cols[$shortName]['type']) {
1725
                case 'decimal':
1726
                    $value = (float) $value;
1727
                    break;
1728
                case 'number':
1729
                    $value = (int) $value;
1730
                    break;
1731
                case 'bool':
1732
                    $value = (bool) $value;
1733
                    break;
1734
            }
1735
        }
1736
        if ((isset($this->_params[$name]) && $this->_params[$name] != $value) && !isset($this->_changedParams[$name])) {
1737
            $this->_changedParams[$name] = $this->_params[$name];
1738
        }
1739
1740
        $this->_params[$name] = $value;
1741
    }
1742
1743
    /**
1744
     * Isset handler for model params
1745
     * 
1746
     * @param string $name
1747
     * @return boolean
1748
     */
1749
    public function __isset($name)
1750
    {
1751
        static::fixPrefix($name);
1752
        return isset($this->_params[$name]);
1753
    }
1754
1755
    /**
1756
     * Convert object to string
1757
     * 
1758
     * @return string
1759
     */
1760
    public function __toString()
1761
    {
1762
        return $this->name();
1763
    }
1764
1765
}
1766