Item   F
last analyzed

Complexity

Total Complexity 76

Size/Duplication

Total Lines 448
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 448
rs 2.2388
c 0
b 0
f 0
wmc 76

12 Methods

Rating   Name   Duplication   Size   Complexity  
A realType() 0 14 4
C simpleItemHandler() 0 49 8
C isVisible() 0 55 18
A beforeSave() 0 2 1
B afterDelete() 0 18 6
A getHref() 0 2 1
A getPrice() 0 5 4
A indexes() 0 18 1
B relations() 0 39 1
C afterSave() 0 51 24
B name() 0 8 5
A inFav() 0 7 3

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/**
4
 * Item
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
12
namespace Inji\Ecommerce;
13
/**
14
 * @property int $id
15
 * @property int $category_id
16
 * @property int $image_file_id
17
 * @property string $name
18
 * @property string $subtitle
19
 * @property string $alias
20
 * @property string $description
21
 * @property int $item_type_id
22
 * @property bool $best
23
 * @property int $item_badge_id
24
 * @property bool $deleted
25
 * @property int $user_id
26
 * @property int $weight
27
 * @property int $sales
28
 * @property string $imported
29
 * @property string $tree_path
30
 * @property string $search_index
31
 * @property string $date_create
32
 * @property string $widget
33
 * @property string $view
34
 *
35
 * @property-read \Ecommerce\Item\Badge $badge
36
 * @property-read \Ecommerce\Category $category
37
 * @property-read \Ecommerce\Item\Param[] $options
38
 * @property-read \Ecommerce\Item\Offer[] $offers
39
 * @method  \Ecommerce\Item\Offer[] offers($options)
40
 * @property-read \Ecommerce\Item\Type $type
41
 * @property-read \Files\File $image
42
 * @property-read \Ecommerce\Item\Image[] $images
43
 * @property-read \Users\User $user
44
 */
45
class Item extends \Inji\Model {
46
47
    public static $categoryModel = 'Ecommerce\Category';
48
    public static $objectName = 'Товар';
49
    public static $labels = [
50
        'name' => 'Название',
51
        'title' => 'Торговое название',
52
        'subtitle' => 'Подзаголовок',
53
        'alias' => 'Алиас',
54
        'item_badge_id' => 'Наклейка',
55
        'category_id' => 'Раздел',
56
        'description' => 'Описание',
57
        'item_type_id' => 'Тип товара',
58
        'image_file_id' => 'Изображение',
59
        'best' => 'Лучшее предложение',
60
        'options' => 'Параметры',
61
        'offers' => 'Торговые предложения',
62
        'widget' => 'Виджет для отображения в каталоге',
63
        'view' => 'Шаблон для отображения полной информации',
64
        'deleted' => 'Удален',
65
        'imgs' => 'Фото',
66
        'date_create' => 'Дата создания'
67
    ];
68
    public static $cols = [
69
        //Основные параметры
70
        'category_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'category'],
71
        'image_file_id' => ['type' => 'image'],
72
        'name' => ['type' => 'text'],
73
        'title' => ['type' => 'text'],
74
        'subtitle' => ['type' => 'text'],
75
        'alias' => ['type' => 'text'],
76
        'description' => ['type' => 'html'],
77
        'item_type_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'type'],
78
        'best' => ['type' => 'bool'],
79
        'item_badge_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'badge'],
80
        'deleted' => ['type' => 'bool'],
81
        //Системные
82
        'user_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'user'],
83
        'weight' => ['type' => 'number'],
84
        'sales' => ['type' => 'number', 'logging' => false],
85
        'visible' => ['type' => 'number', 'logging' => false],
86
        'imported' => ['type' => 'text'],
87
        'tree_path' => ['type' => 'text'],
88
        'search_index' => ['type' => 'text', 'logging' => false],
89
        'date_create' => ['type' => 'dateTime'],
90
        'widget' => ['type' => 'text'],
91
        'view' => ['type' => 'text', 'logging' => false],
92
        //Менеджеры
93
        'options' => ['type' => 'dataManager', 'relation' => 'options'],
94
        'offers' => ['type' => 'dataManager', 'relation' => 'offers'],
95
        'imgs' => ['type' => 'dataManager', 'relation' => 'images'],
96
    ];
97
98
    public static function simpleItemHandler($request) {
99
        if ($request) {
100
            $item = new Item();
101
            $item->name = $request['name'];
102
            $item->description = $request['description'];
103
            $item->category_id = $request['category'];
104
            $item->save();
105
            if (!empty($_FILES['ActiveForm_simpleItem']['tmp_name']['Ecommerce\Item']['image'])) {
106
                $file_id = \App::$primary->files->upload([
107
                    'tmp_name' => $_FILES['ActiveForm_simpleItem']['tmp_name']['Ecommerce\Item']['image'],
108
                    'name' => $_FILES['ActiveForm_simpleItem']['name']['Ecommerce\Item']['image'],
109
                    'type' => $_FILES['ActiveForm_simpleItem']['type']['Ecommerce\Item']['image'],
110
                    'size' => $_FILES['ActiveForm_simpleItem']['size']['Ecommerce\Item']['image'],
111
                    'error' => $_FILES['ActiveForm_simpleItem']['error']['Ecommerce\Item']['image'],
112
                ], [
113
                    'upload_code' => 'activeForm:' . 'Ecommerce\Item' . ':' . $item->pk(),
114
                    'accept_group' => 'image'
115
                ]);
116
                if ($file_id) {
117
                    $item->image_file_id = $file_id;
118
                    $item->save();
119
                }
120
            }
121
            if (!empty($request['options']['option'])) {
122
                foreach ($request['options']['option'] as $key => $option_id) {
123
                    $param = new Item\Param();
124
                    $param->item_id = $item->id;
125
                    $param->value = $request['options']['value'][$key];
126
                    $param->item_option_id = $option_id;
127
                    $param->save();
128
                }
129
            }
130
            $offer = new Item\Offer();
131
            $offer->item_id = $item->id;
132
            $offer->save();
133
            if (!empty($request['offerOptions']['option'])) {
134
                foreach ($request['offerOptions']['option'] as $key => $option_id) {
135
                    $param = new Item\Offer\Param();
136
                    $param->item_offer_id = $offer->id;
137
                    $param->value = $request['offerOptions']['value'][$key];
138
                    $param->item_offer_option_id = $option_id;
139
                    $param->save();
140
                }
141
            }
142
            $price = new Item\Offer\Price();
143
            $price->price = $request['price'];
144
            $price->item_offer_id = $offer->id;
145
            $price->currency_id = $request['currency'];
146
            $price->save();
147
        }
148
    }
149
150
    public static $dataManagers = [
151
        'manager' => [
152
            'name' => 'Товары',
153
            'cols' => [
154
                'name',
155
                'imgs',
156
                'category_id',
157
                'item_type_id',
158
                'best',
159
                'deleted',
160
                'options',
161
                'offers',
162
                'date_create'
163
            ],
164
            'categorys' => [
165
                'model' => 'Ecommerce\Category',
166
            ],
167
            'sortable' => ['date_create'],
168
            'preSort' => [
169
                'date_create' => 'desc'
170
            ],
171
            'filters' => [
172
                'id', 'name', 'best', 'deleted', 'date_create'
173
            ],
174
            'sortMode' => true
175
        ]
176
    ];
177
    public static $forms = [
178
        'manager' => [
179
            'map' => [
180
                ['name', 'alias'],
181
                ['title', 'subtitle'],
182
                ['category_id', 'item_type_id', 'deleted'],
183
                ['widget', 'view'],
184
                ['best', 'item_badge_id', 'image_file_id'],
185
                ['description'],
186
                ['imgs'],
187
                ['options'],
188
                ['offers'],
189
            ]
190
        ],
191
        'simpleItem' => [
192
            'options' => [
193
                'access' => [
194
                    'groups' => [
195
                        3
196
                    ]
197
                ],
198
            ],
199
            'name' => 'Простой товар с ценой',
200
            'inputs' => [
201
                'name' => ['type' => 'text'],
202
                'description' => ['type' => 'html'],
203
                'category' => ['type' => 'select', 'source' => 'model', 'model' => 'Ecommerce\Category', 'label' => 'Категория'],
204
                'image' => ['type' => 'image', 'label' => 'Изображение'],
205
                'price' => ['type' => 'text', 'label' => 'Цена'],
206
                'currency' => ['type' => 'select', 'source' => 'model', 'model' => 'Money\Currency', 'label' => 'Валюта'],
207
                'options' => ['type' => 'dynamicList', 'source' => 'options', 'options' => [
208
                    'inputs' => [
209
                        'option' => ['type' => 'select', 'source' => 'model', 'model' => 'Ecommerce\Item\Option', 'label' => 'Свойство'],
210
                        'value' => ['type' => 'dynamicType', 'typeSource' => 'selfMethod', 'selfMethod' => 'realType', 'label' => 'Значение'],
211
                    ]
212
                ]
213
                ],
214
                'offerOptions' => ['type' => 'dynamicList', 'source' => 'options', 'options' => [
215
                    'inputs' => [
216
                        'option' => ['type' => 'select', 'source' => 'model', 'model' => 'Ecommerce\Item\Offer\Option', 'label' => 'Свойство предложения'],
217
                        'value' => ['type' => 'dynamicType', 'typeSource' => 'selfMethod', 'selfMethod' => 'realType', 'label' => 'Значение'],
218
                    ]
219
                ], 'label' => 'Параметры предложения'
220
                ]
221
            ],
222
            'map' => [
223
                ['name', 'category'],
224
                ['description'],
225
                ['image'],
226
                ['price', 'currency'],
227
                ['options'],
228
                ['offerOptions'],
229
            ],
230
            'handler' => 'simpleItemHandler'
231
        ]
232
    ];
233
234
    public function realType() {
235
        if ($this->option && $this->option->type) {
0 ignored issues
show
Bug Best Practice introduced by
The property option does not exist on Inji\Ecommerce\Item. Since you implemented __get, consider adding a @property annotation.
Loading history...
236
            $type = $this->option->type;
237
238
            if ($type == 'select') {
239
                return [
240
                    'type' => 'select',
241
                    'source' => 'relation',
242
                    'relation' => 'option:items',
243
                ];
244
            }
245
            return $type;
246
        }
247
        return 'text';
248
    }
249
250
    public static function indexes() {
251
        return [
252
            'ecommerce_item_item_category_id' => [
253
                'type' => 'INDEX',
254
                'cols' => [
255
                    'item_category_id'
256
                ]
257
            ],
258
            'inji_ecommerce_item_item_tree_path' => [
259
                'type' => 'INDEX',
260
                'cols' => [
261
                    'item_tree_path(255)'
262
                ]
263
            ],
264
            'ecommerce_item_item_search_index' => [
265
                'type' => 'INDEX',
266
                'cols' => [
267
                    'item_search_index(255)'
268
                ]
269
            ],
270
        ];
271
    }
272
273
    public function isVisible() {
274
        if ($this->deleted) {
275
            return false;
276
        }
277
        if (empty(\App::$cur->Ecommerce->config['view_empty_image']) && !$this->image_file_id) {
278
            return false;
279
        }
280
        if (empty(\App::$cur->Ecommerce->config['view_empty_warehouse'])) {
281
            $warehouseIds = \Ecommerce\OptionsParser::getWarehouses();
282
            $selectOptions = ['where' => [['item_offer_item_id', $this->id]]];
283
            $selectOptions['where'][] = [
284
                '(
285
          (SELECT COALESCE(sum(`' . Item\Offer\Warehouse::colPrefix() . 'count`),0) 
286
            FROM ' . \App::$cur->db->table_prefix . Item\Offer\Warehouse::table() . ' iciw 
287
            WHERE iciw.' . Item\Offer\Warehouse::colPrefix() . Item\Offer::index() . ' = ' . Item\Offer::index() . '
288
                ' . ($warehouseIds ? ' AND iciw.' . Item\Offer\Warehouse::colPrefix() . Warehouse::index() . ' IN(' . implode(',', $warehouseIds) . ')' : '') . '
289
            )
290
          -
291
          (SELECT COALESCE(sum(' . Warehouse\Block::colPrefix() . 'count) ,0)
292
            FROM ' . \App::$cur->db->table_prefix . Warehouse\Block::table() . ' iewb
293
            inner JOIN ' . \App::$cur->db->table_prefix . Cart::table() . ' icc ON icc.' . Cart::index() . ' = iewb.' . Warehouse\Block::colPrefix() . Cart::index() . ' AND (
294
                (`' . Cart::colPrefix() . 'warehouse_block` = 1 and `' . Cart::colPrefix() . 'cart_status_id` in(2,3,6)) ||
295
                (`' . Cart::colPrefix() . Cart\Status::index() . '` in(0,1) and `' . Cart::colPrefix() . 'date_last_activ` >=subdate(now(),INTERVAL 30 MINUTE))
296
            )
297
            WHERE iewb.' . Warehouse\Block::colPrefix() . Item\Offer::index() . ' = ' . Item\Offer::index() . ')
298
          )',
299
                0,
300
                '>'
301
            ];
302
            if (!Item\Offer::getList($selectOptions)) {
303
                return false;
304
            }
305
        }
306
        $isset = empty(\App::$cur->Ecommerce->config['available_price_types']);
307
        $nozero = !empty(\App::$cur->Ecommerce->config['show_without_price']);
308
        if (!$isset || !$nozero) {
309
            $this->loadRelation('offers');
310
            foreach ($this->offers as $offer) {
311
                foreach ($offer->prices as $price) {
312
                    if (empty(\App::$cur->Ecommerce->config['available_price_types']) || in_array($price->item_offer_price_type_id, \App::$cur->Ecommerce->config['available_price_types'])) {
313
                        $isset = true;
314
                    }
315
                    if ($price->price > 0) {
316
                        $nozero = true;
317
                    }
318
                    if ($isset && $nozero) {
319
                        break 2;
320
                    }
321
                }
322
            }
323
            if (!$isset || !$nozero) {
324
                return false;
325
            }
326
        }
327
        return true;
328
    }
329
330
    public function beforeSave() {
331
        $this->visible = $this->isVisible();
0 ignored issues
show
Bug Best Practice introduced by
The property visible does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
332
    }
333
334
    public function afterSave() {
335
        $itemId = $this->id;
336
        if ($this->category_id && isset($this->_changedParams['item_visible'])) {
337
            $categoryId = $this->category_id;
338
            \App::$primary->daemon->task(function () use ($categoryId) {
339
                $category = \Ecommerce\Category::get($categoryId);
0 ignored issues
show
Bug introduced by
The type Ecommerce\Category was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
340
                if ($category) {
341
                    $category->calcItemsCount();
342
                }
343
            });
344
        }
345
        \App::$primary->daemon->task(function () use ($itemId) {
346
            $item = \Ecommerce\Item::get($itemId);
0 ignored issues
show
Bug introduced by
The type Ecommerce\Item was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
347
            if (!$item) {
348
                return;
349
            }
350
            $item->search_index = $item->name . ' ';
351
            if ($item->category) {
352
                $item->search_index .= $item->category->name . ' ';
353
            }
354
            $category = $item->category;
355
            if ($category) {
356
                $categoryOptions = $category->options(['key' => 'item_option_id']);
357
            } else {
358
                $categoryOptions = [];
359
            }
360
            foreach ($item->options as $option) {
361
                if ($option->item_option_searchable && $option->value) {
362
                    if ($option->item_option_type != 'select') {
363
                        $item->search_index .= $option->value . ' ';
364
                    } elseif (!empty($option->option->items[$option->value])) {
365
                        $item->search_index .= $option->option->items(['where' => ['id', $option->value]])[$option->value]->value . ' ';
366
                    }
367
                }
368
                if ($option->item_option_view && !isset($categoryOptions[$option->item_option_id])) {
369
                    $item->category->addRelation('options', $option->item_option_id);
370
                    $categoryOptions = $item->category->options(['key' => 'item_option_id']);
371
                } elseif (!$option->item_option_view && isset($categoryOptions[$option->item_option_id])) {
372
                    $categoryOptions[$option->item_option_id]->delete();
373
                    unset($categoryOptions[$option->item_option_id]);
374
                }
375
            }
376
            if ($item->offers) {
377
                foreach ($item->offers as $offer) {
378
                    if ($offer->options) {
379
                        foreach ($offer->options as $option) {
380
                            if ($option->item_offer_option_searchable && $option->value) {
381
                                if ($option->item_offer_option_type != 'select') {
382
                                    $item->search_index .= $option->value . ' ';
383
                                } elseif (!empty($option->option->items[$option->value])) {
384
                                    $item->search_index .= $option->option->items[$option->value]->value . ' ';
385
                                }
386
                            }
387
                        }
388
                    }
389
                }
390
            }
391
        });
392
393
    }
394
395
    public static function relations() {
396
397
        return [
398
            'badge' => [
399
                'model' => 'Inji\Ecommerce\Item\Badge',
400
                'col' => 'item_badge_id'
401
            ],
402
            'category' => [
403
                'model' => 'Inji\Ecommerce\Category',
404
                'col' => 'category_id'
405
            ],
406
            'options' => [
407
                'type' => 'many',
408
                'model' => 'Inji\Ecommerce\Item\Param',
409
                'col' => 'item_id',
410
                'resultKey' => 'item_option_id',
411
                'join' => [Item\Option::table(), Item\Option::index() . ' = ' . Item\Param::colPrefix() . Item\Option::index()]
412
            ],
413
            'offers' => [
414
                'type' => 'many',
415
                'model' => 'Inji\Ecommerce\Item\Offer',
416
                'col' => 'item_id',
417
            ],
418
            'type' => [
419
                'model' => 'Inji\Ecommerce\Item\Type',
420
                'col' => 'item_type_id',
421
            ],
422
            'image' => [
423
                'model' => 'Inji\Files\File',
424
                'col' => 'image_file_id'
425
            ],
426
            'images' => [
427
                'type' => 'many',
428
                'model' => 'Inji\Ecommerce\Item\Image',
429
                'col' => 'item_id'
430
            ],
431
            'user' => [
432
                'model' => 'Inji\Users\User',
433
                'col' => 'user_id'
434
            ]
435
        ];
436
    }
437
438
    /**
439
     * @param int $offerId
440
     * @param bool|\Ecommerce\Cart $cart
441
     * @return bool|Item\Offer\Price|null
442
     */
443
    public function getPrice($offerId = 0, $cart = false) {
444
        if ($offerId) {
445
            return $this->offers ? $this->offers[$offerId]->getPrice($cart) : false;
446
        }
447
        return $this->offers(['key' => false]) ? $this->offers(['key' => false])[0]->getPrice($cart) : false;
448
    }
449
450
    public function name() {
451
        if (!empty(\Inji\App::$primary->ecommerce->config['item_option_as_name'])) {
0 ignored issues
show
Bug Best Practice introduced by
The property ecommerce does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
452
            $param = Item\Param::get([['item_id', $this->id], ['item_option_id', \App::$primary->ecommerce->config['item_option_as_name']]]);
453
            if ($param && $param->value) {
454
                return $param->value;
455
            }
456
        }
457
        return $this->title ? $this->title : $this->name;
0 ignored issues
show
Bug Best Practice introduced by
The property title does not exist on Inji\Ecommerce\Item. Since you implemented __get, consider adding a @property annotation.
Loading history...
458
    }
459
460
    public function afterDelete() {
461
        if (!$this->id) {
462
            return;
463
        }
464
        $itemId = $this->id;
465
        \Inji\App::$primary->daemon->task(function () use ($itemId) {
0 ignored issues
show
Bug introduced by
The method task() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

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

465
        \Inji\App::$primary->daemon->/** @scrutinizer ignore-call */ 
466
                                     task(function () use ($itemId) {
Loading history...
Bug Best Practice introduced by
The property daemon does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method task() does not exist on null. ( Ignorable by Annotation )

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

465
        \Inji\App::$primary->daemon->/** @scrutinizer ignore-call */ 
466
                                     task(function () use ($itemId) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
466
            $item = \Ecommerce\Item::get($itemId);
467
            foreach ($item->options as $option) {
468
                $option->delete();
469
            }
470
            foreach ($item->offers as $offer) {
471
                $offer->delete();
472
            }
473
            foreach ($item->images as $image) {
474
                $image->delete();
475
            }
476
            if ($item->image) {
477
                $item->image->delete();
478
            }
479
        });
480
    }
481
482
    public function getHref() {
483
        return "/ecommerce/view/{$this->pk()}";
484
    }
485
486
    public function inFav() {
487
        if (\Users\User::$cur->id) {
488
            $fav = \Ecommerce\Favorite::get([['user_id', \Users\User::$cur->id], ['item_id', $this->id]]);
0 ignored issues
show
Bug introduced by
The type Ecommerce\Favorite was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
489
            return (bool) $fav;
490
        } else {
491
            $favs = !empty($_COOKIE['ecommerce_favitems']) ? json_decode($_COOKIE['ecommerce_favitems'], true) : [];
492
            return in_array($this->id, $favs);
493
        }
494
    }
495
}