GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

BackendProductController   F
last analyzed

Complexity

Total Complexity 83

Size/Duplication

Total Lines 714
Duplicated Lines 22.97 %

Coupling/Cohesion

Components 1
Dependencies 34

Importance

Changes 0
Metric Value
wmc 83
lcom 1
cbo 34
dl 164
loc 714
rs 1.886
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A behaviors() 14 14 1
B actions() 27 81 7
A actionIndex() 0 21 2
F actionEdit() 15 141 22
F actionGenerate() 34 141 18
C actionClone() 47 123 13
A actionDelete() 0 24 5
A actionRemoveAll() 0 13 3
A sortModels() 19 19 2
B generateOptions() 0 24 6
A generateCase() 8 8 2
A actionAjaxRelatedProduct() 0 23 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BackendProductController 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 BackendProductController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace app\modules\shop\controllers;
4
5
use app\backend\actions\PropertyHandler;
6
use app\backend\components\BackendController;
7
use app\backend\events\BackendEntityEditEvent;
8
use app\modules\image\widgets\views\AddImageAction;
9
use app\modules\shop\models\Category;
10
use app\modules\image\models\Image;
11
use app\models\BaseObject;
12
use app\models\ObjectPropertyGroup;
13
use app\modules\shop\models\Currency;
14
use app\modules\shop\models\Product;
15
use app\models\Property;
16
use app\models\PropertyStaticValues;
17
use app\models\ViewObject;
18
use app\properties\HasProperties;
19
use app\modules\image\widgets\RemoveAction;
20
use app\modules\image\widgets\SaveInfoAction;
21
use app\modules\image\widgets\UploadAction;
22
use app\backend\actions\UpdateEditable;
23
use devgroup\JsTreeWidget\AdjacencyFullTreeDataAction;
24
use Yii;
25
use yii\data\ActiveDataProvider;
26
use yii\db\Query;
27
use yii\filters\AccessControl;
28
use yii\helpers\ArrayHelper;
29
use yii\helpers\Json;
30
use yii\helpers\Url;
31
use yii\web\NotFoundHttpException;
32
use yii\web\ServerErrorHttpException;
33
use app\backend\actions\MassPublishAction;
34
use app\backend\actions\CategoryMovementsAction;
35
use app\modules\shop\actions\BatchEditPriceAction;
36
37
class BackendProductController extends BackendController
38
{
39
    const EVENT_BACKEND_PRODUCT_EDIT = 'backend-product-edit';
40
    const EVENT_BACKEND_PRODUCT_EDIT_SAVE = 'backend-product-edit-save';
41
    const EVENT_BACKEND_PRODUCT_EDIT_FORM = 'backend-product-edit-form';
42
    const EVENT_BACKEND_PRODUCT_AFTER_SAVE = 'backend-product-after-save';
43
44
    /**
45
     * @inheritdoc
46
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<strin...g,boolean|string[]>[]>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
47
     */
48 View Code Duplication
    public function behaviors()
49
    {
50
        return [
51
            'access' => [
52
                'class' => AccessControl::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
53
                'rules' => [
54
                    [
55
                        'allow' => true,
56
                        'roles' => ['product manage'],
57
                    ],
58
                ],
59
            ],
60
        ];
61
    }
62
63
    /**
64
     * @inheritdoc
65
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
66
     */
67
    public function actions()
68
    {
69
        $product = Yii::$container->get(Product::class);
70
        return [
71
            'getTree' => [
72
                'class' => AdjacencyFullTreeDataAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
73
                'class_name' => Category::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
74
                'model_label_attribute' => 'name',
75
            ],
76
            'getCatTree' => [
77
                'class' => 'app\backend\actions\JSSelectableTreeGetTree',
78
                'modelName' => 'app\modules\shop\models\Category',
79
                'label_attribute' => 'name',
80
                'vary_by_type_attribute' => null,
81
            ],
82
            'addImage' => [
83
                'class' => AddImageAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
84
            ],
85
            'upload' => [
86
                'class' => UploadAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
87
                'upload' => 'theme/resources/product-images',
88
            ],
89
            'remove' => [
90
                'class' => RemoveAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
91
                'uploadDir' => 'theme/resources/product-images',
92
            ],
93
            'save-info' => [
94
                'class' => SaveInfoAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
95
            ],
96
            'update-editable' => [
97
                'class' => UpdateEditable::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
98
                'modelName' => get_class($product),
99
                'allowedAttributes' => [
100 View Code Duplication
                    'currency_id' => function (Product $model, $attribute) {
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
101
                        if ($model === null || $model->currency === null || $model->currency_id === 0) {
0 ignored issues
show
Bug introduced by
The property currency does not seem to exist. Did you mean currency_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
102
                            return null;
103
                        }
104
                        return \yii\helpers\Html::tag(
105
                            'div',
106
                            $model->currency->name,
0 ignored issues
show
Bug introduced by
The property currency does not seem to exist. Did you mean currency_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
107
                            ['class' => $model->currency->name]
0 ignored issues
show
Bug introduced by
The property currency does not seem to exist. Did you mean currency_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
108
                        );
109
                    },
110
                    'price',
111
                    'old_price',
112 View Code Duplication
                    'active' => function (Product $model) {
113
                        if ($model === null || $model->active === null) {
114
                            return null;
115
                        }
116
                        if ($model->active === 1) {
117
                            $label_class = 'label-success';
118
                            $value = 'Active';
119
                        } else {
120
                            $value = 'Inactive';
121
                            $label_class = 'label-default';
122
                        }
123
                        return \yii\helpers\Html::tag(
124
                            'span',
125
                            Yii::t('app', $value),
126
                            ['class' => "label $label_class"]
127
                        );
128
                    },
129
                ],
130
            ],
131
            'property-handler' => [
132
                'class' => PropertyHandler::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
133
                'modelName' => get_class($product)
134
            ],
135
            'publish-switch' => [
136
                'class' => MassPublishAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
137
                'modelName' => get_class($product),
138
                'attribute' => 'active',
139
            ],
140
            'categoryMovements' => [
141
                'class' => CategoryMovementsAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
142
            ],
143
            'batch-edit-price' => [
144
                'class' => BatchEditPriceAction::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
145
            ]
146
        ];
147
    }
148
149
    /**
150
     * @return string
151
     * @throws ServerErrorHttpException
152
     */
153
    public function actionIndex()
154
    {
155
        $product = Yii::$container->get(Product::class);
156
        $searchModel = $product;
157
        $params = Yii::$app->request->get();
158
        /** @var ActiveDataProvider $dataProvider */
159
        $dataProvider = $searchModel->search($params);
160
        if (null !== $catId = Yii::$app->request->get('parent_id')) {
161
            $dataProvider->query->leftJoin(
162
                BaseObject::getForClass(get_class($product))->categories_table_name . ' cp',
163
                'cp.object_model_id=product.id'
164
            )->andWhere('product.parent_id=0 AND cp.category_id=:cur', [':cur' => $catId]);
165
        }
166
        return $this->render(
167
            'index',
168
            [
169
                'dataProvider' => $dataProvider,
170
                'searchModel' => $searchModel,
171
            ]
172
        );
173
    }
174
175
    /**
176
     * @param null $id
177
     * @return string|\yii\web\Response
178
     * @throws NotFoundHttpException
179
     * @throws ServerErrorHttpException
180
     * @throws \Exception
181
     * @throws \yii\base\InvalidRouteException
182
     */
183
    public function actionEdit($id = null)
184
    {
185
        $product = Yii::$container->get(Product::class);
186
        /*
187
         * @todo Продумать механизм сохранения изображений для нового продукта.
188
         * Сейчас для нового продукта скрывается форма добавления изображений.
189
         */
190
        if (null === $object = BaseObject::getForClass($product::className())) {
191
            throw new ServerErrorHttpException;
192
        }
193
194
        /** @var null|Product|HasProperties|\devgroup\TagDependencyHelper\ActiveRecordHelper $model */
195
        $model = null;
196
        $parent = null;
197
        if (null === $id) {
198
            $model = $product;
199
            $model->loadDefaultValues();
200
            $model->currency_id = Currency::getMainCurrency()->id;
201
            $parent_id = Yii::$app->request->get('owner_id', 0);
202 View Code Duplication
            if (0 !== intval($parent_id) && null !== $product::findById($parent_id)) {
203
                $model->parent_id = $parent_id;
204
            }
205
            $model->measure_id = $this->module->defaultMeasureId;
206
        } else {
207
            $model = $product::findById($id, null);
208 View Code Duplication
            if ((null !== $model) && ($model->parent_id > 0)) {
209
                $parent = $product::findById($model->parent_id, null);
210
            }
211
        }
212
213
        if (null === $model) {
214
            throw new NotFoundHttpException();
215
        }
216
217
        $model->loadRelatedProductsArray();
218
219
        $event = new BackendEntityEditEvent($model);
220
        $this->trigger(self::EVENT_BACKEND_PRODUCT_EDIT, $event);
221
222
        $post = \Yii::$app->request->post();
223
224
        if ($event->isValid && $model->load($post)) {
225
            $saveStateEvent = new BackendEntityEditEvent($model);
226
            $this->trigger(self::EVENT_BACKEND_PRODUCT_EDIT_SAVE, $saveStateEvent);
227
228
            if ($model->validate()) {
229
                if (isset($post['GeneratePropertyValue'])) {
230
                    $generateValues = $post['GeneratePropertyValue'];
231
                } else {
232
                    $generateValues = [];
233
                }
234
                if (isset($post['PropertyGroup'])) {
235
                    $model->option_generate = Json::encode(
236
                        [
237
                            'group' => $post['PropertyGroup']['id'],
238
                            'values' => $generateValues
239
                        ]
240
                    );
241
                } else {
242
                    $model->option_generate = '';
243
                }
244
245
                $save_result = $model->save();
246
                $model->saveProperties($post);
247
                $model->saveRelatedProducts();
248
249 View Code Duplication
                if (null !== $view_object = ViewObject::getByModel($model, true)) {
250
                    if ($view_object->load($post, 'ViewObject')) {
251
                        if ($view_object->view_id <= 0) {
252
                            $view_object->delete();
253
                        } else {
254
                            $view_object->save();
255
                        }
256
                    }
257
                }
258
259
                if ($save_result) {
260
                    $modelAfterSaveEvent = new BackendEntityEditEvent($model);
261
                    $this->trigger(self::EVENT_BACKEND_PRODUCT_AFTER_SAVE, $modelAfterSaveEvent);
262
263
                    $categories = isset($post[$model->formName()]['categories']) ? $post[$model->formName()]['categories'] : [];
264
265
                    $model->saveCategoriesBindings($categories);
266
267
                    $this->runAction('save-info', ['model_id'=>$model->id]);
268
                    $model->invalidateTags();
269
270
271
                    $action = Yii::$app->request->post('action', 'save');
272
                    if (Yii::$app->request->post(HasProperties::FIELD_ADD_PROPERTY_GROUP)
273
                        || Yii::$app->request->post(HasProperties::FIELD_REMOVE_PROPERTY_GROUP)) {
274
                        $action = 'save';
275
                    }
276
                    $returnUrl = Yii::$app->request->get('returnUrl', ['index']);
277
                    switch ($action) {
278
                        case 'next':
279
                            return $this->redirect(
280
                                [
281
                                    'edit',
282
                                    'returnUrl' => $returnUrl,
283
                                    'parent_id' => Yii::$app->request->get('parent_id', null)
284
                                ]
285
                            );
286
                        case 'back':
287
                            return $this->redirect($returnUrl);
288
                        default:
289
                            return $this->redirect(
290
                                Url::toRoute([
291
                                    'edit',
292
                                    'id' => $model->id,
293
                                    'returnUrl' => $returnUrl,
294
                                    'parent_id' => $model->main_category_id
295
                                ])
296
                            );
297
                    }
298
                } else {
299
                    Yii::$app->session->setFlash('error', Yii::t('app', 'Cannot save data'));
300
                }
301
            } else {
302
                Yii::$app->session->setFlash('error', Yii::t('app', 'Cannot save data'));
303
            }
304
        }
305
306
        $items = ArrayHelper::map(
307
            Category::find()->all(),
308
            'id',
309
            'name'
310
        );
311
312
313
        return $this->render(
314
            'product-form',
315
            [
316
                'object' => $object,
317
                'model' => $model,
318
                'items' => $items,
319
                'selected' => $model->getCategoryIds(),
320
                'parent' => $parent,
321
            ]
322
        );
323
    }
324
325
    /**
326
     * @param $id
327
     * @throws NotFoundHttpException
328
     * @throws \yii\db\Exception
329
     */
330
    public function actionGenerate($id)
331
    {
332
        $product = Yii::$container->get(Product::class);
333
        $post = \Yii::$app->request->post();
334
        if (!isset($post['GeneratePropertyValue'])) {
335
            throw new NotFoundHttpException();
336
        }
337
        $parent = $product::findById($id, null);
338
        if ($parent === null) {
339
            throw new NotFoundHttpException();
340
        }
341
342
        $object = BaseObject::getForClass(get_class($product));
343
        $catIds = (new Query())->select('category_id')->from([$object->categories_table_name])->where(
344
            'object_model_id = :id',
345
            [':id' => $id]
346
        )->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])->column();
347
348
349
        if (isset($post['GeneratePropertyValue'])) {
350
            $generateValues = $post['GeneratePropertyValue'];
351
            if(empty($parent->propertyGroups[$post['PropertyGroup']['id']])){
352
                $post[HasProperties::FIELD_ADD_PROPERTY_GROUP]['Product'] = $post['PropertyGroup']['id'];
353
            }
354
        } else {
355
            $generateValues = [];
356
        }
357
        $parent->option_generate = Json::encode(
358
            [
359
                'group' => $post['PropertyGroup']['id'],
360
                'values' => $generateValues
361
            ]
362
        );
363
        $parent->save();
364
365
        $postProperty = [];
366
        foreach ($post['GeneratePropertyValue'] as $key_property => $values) {
367
            $inner = [];
368
            foreach ($values as $key_value => $trash) {
369
                $inner[] = [$key_property => $key_value];
370
            }
371
            $postProperty[] = $inner;
372
        }
373
374
        $optionProperty = self::generateOptions($postProperty);
375
376
        foreach ($optionProperty as $option) {
377
            $product = Yii::$container->get(Product::class);
378
            /** @var Product|HasProperties $model */
379
            $model = $product;
380
            $model->load($post);
0 ignored issues
show
Bug introduced by
The method load does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
381
382
            $model->parent_id = $parent->id;
383
            $nameAppend = [];
384
            $slugAppend = [];
385
            $tempPost = [];
386
387
            // @todo something
388
            foreach ($option as $optionValue) {
389
                $valueModels = [];
390
                foreach ($optionValue as $propertyKey => $propertyValue) {
391
                    if (false === in_array($propertyKey, $valueModels)) {
392
                        /** @var  $propertyStaticValues  PropertyStaticValues */
393
                        $propertyStaticValues = PropertyStaticValues::findOne($propertyValue);
394
                        $key = $propertyStaticValues->property->key;
395
                        $tempPost[$key] = $propertyValue;
396
                        $valueModels[] = $propertyKey;
397
                        $nameAppend[] = $propertyStaticValues->name;
398
                        $slugAppend[] = $propertyStaticValues->id;
399
                    }
400
                }
401
            }
402
            $model->measure_id = $parent->measure_id;
403
            $model->name = $parent->name . ' (' . implode(', ', $nameAppend) . ')';
404
            $model->slug = $parent->slug . '-' . implode('-', $slugAppend);
405
            $save_model = $model->save();
0 ignored issues
show
Bug introduced by
The method save does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
406
            if ($save_model) {
407
                $groups = [
408
                    [$parent->object->id, $model->id, $post['PropertyGroup']['id']]
409
                ];
410
                foreach (array_keys($parent->propertyGroups) as $key) {
411
                    if ($key === $post['PropertyGroup']['id']) {
412
                        continue;
413
                    }
414
                    $groups[] = [$parent->object->id, $model->id, $key];
415
                }
416
                Yii::$app->db->createCommand()->batchInsert(
417
                    ObjectPropertyGroup::tableName(),
418
                    ['object_id', 'object_model_id', 'property_group_id'],
419
                    $groups
420
                )->execute();
421
                $newValues = array_merge($parent->abstractModel->attributes, $tempPost);
422
                $model->saveProperties([
0 ignored issues
show
Bug introduced by
The method saveProperties does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Product.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
423
                    'Properties_Product_' . $model->id => $newValues,
424
                ]);
425
                $add = [];
426
                foreach ($catIds as $value) {
427
                    $add[] = [
428
                        $value,
429
                        $model->id
430
                    ];
431
                }
432 View Code Duplication
                if (!empty($add)) {
433
                    Yii::$app->db->createCommand()->batchInsert(
434
                        $object->categories_table_name,
435
                        ['category_id', 'object_model_id'],
436
                        $add
437
                    )->execute();
438
                }
439
440
                $params = $parent->images;
441 View Code Duplication
                if (!empty($params)) {
442
                    $rows = [];
443
                    foreach ($params as $param) {
444
                        $rows[] = [
445
                            $param['object_id'],
446
                            $model->id,
447
                            $param['filename'],
448
                            $param['image_title'],
449
                            $param['image_alt'],
450
                            $param['sort_order'],
451
                        ];
452
                    }
453
454
                    Yii::$app->db->createCommand()->batchInsert(
455
                        Image::tableName(),
456
                        [
457
                            'object_id',
458
                            'object_model_id',
459
                            'filename',
460
                            'image_title',
461
                            'image_alt',
462
                            'sort_order',
463
                        ],
464
                        $rows
465
                    )->execute();
466
467
                }
468
            }
469
        }
470
    }
471
472
    /**
473
     * Clone product action.
474
     * @param integer $id
475
     * @throws \yii\web\NotFoundHttpException
476
     */
477
    public function actionClone($id, $returnUrl = ['index'])
478
    {
479
        $product = Yii::$container->get(Product::class);
480
        /** @var Product|HasProperties $model */
481
        $model = $product::findOne($id);
482
        if ($model === null) {
483
            throw new NotFoundHttpException;
484
        }
485
486
        /** @var Product|HasProperties $newModel */
487
        $newModel = $product;
488
        $newModel->setAttributes($model->attributes, false);
0 ignored issues
show
Bug introduced by
The method setAttributes does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
489
        $time = time();
490
        $newModel->name .= ' (copy ' . date('Y-m-d h:i:s', $time) . ')';
491
        $newModel->slug .= '-copy-' . date('Ymdhis', $time);
492
        $newModel->id = null;
493 View Code Duplication
        if ($newModel->validate() === false) {
0 ignored issues
show
Bug introduced by
The method validate does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
494
            $newModel->slug = substr(uniqid() . "-" . $model->slug, 0, 80);
495
        }
496
        if ($newModel->save()) {
0 ignored issues
show
Bug introduced by
The method save does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
497
            $object = BaseObject::getForClass(get_class($newModel));
498
            // save categories
499
            $categoriesTableName = BaseObject::getForClass(get_class($product))->categories_table_name;
500
            $query = new Query();
501
            $params = $query->select(['category_id', 'sort_order'])->from($categoriesTableName)->where(
502
                ['object_model_id' => $model->id]
503
            )->all();
504
            if (!empty($params)) {
505
                $rows = [];
506
                foreach ($params as $param) {
507
                    $rows[] = [
508
                        $param['category_id'],
509
                        $newModel->id,
510
                        $param['sort_order'],
511
                    ];
512
                }
513
                Yii::$app->db->createCommand()->batchInsert(
514
                    $categoriesTableName,
515
                    [
516
                        'category_id',
517
                        'object_model_id',
518
                        'sort_order'
519
                    ],
520
                    $rows
521
                )->execute();
522
            }
523
524
            // save images bindings
525
            $params = $query->select(
526
                ['object_id', 'filename', 'image_title', 'image_alt', 'sort_order']
527
            )->from(Image::tableName())->where(
528
                [
529
                    'object_id' => $object->id,
530
                    'object_model_id' => $model->id
531
                ]
532
            )->all();
533 View Code Duplication
            if (!empty($params)) {
534
                $rows = [];
535
                foreach ($params as $param) {
536
                    $rows[] = [
537
                        $param['object_id'],
538
                        $newModel->id,
539
                        $param['filename'],
540
                        $param['image_title'],
541
                        $param['image_alt'],
542
                        $param['sort_order'],
543
                    ];
544
                }
545
                Yii::$app->db->createCommand()->batchInsert(
546
                    Image::tableName(),
547
                    [
548
                        'object_id',
549
                        'object_model_id',
550
                        'filename',
551
                        'image_title',
552
                        'image_alt',
553
                        'sort_order',
554
                    ],
555
                    $rows
556
                )->execute();
557
            }
558
            $newModelProps = [];
559 View Code Duplication
            foreach (array_keys($model->propertyGroups) as $key) {
560
                $opg = new ObjectPropertyGroup();
561
                $opg->attributes = [
562
                    'object_id' => $object->id,
563
                    'object_model_id' => $newModel->id,
564
                    'property_group_id' => $key,
565
                ];
566
                $opg->save();
567
                $props = Property::getForGroupId($key);
568
                foreach ($props as $prop) {
569
                    $propValues = $model->getPropertyValuesByPropertyId($prop->id);
0 ignored issues
show
Bug introduced by
The method getPropertyValuesByPropertyId does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Product.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
570
                    if ($propValues !== null) {
571
                        foreach ($propValues->values as $val) {
572
                            $valueToSave = ArrayHelper::getValue($val, 'psv_id', $val['value']);
573
                            $newModelProps[$prop->key][] = $valueToSave;
574
                        }
575
                    }
576
                }
577
            }
578
            $newModel->saveProperties(
0 ignored issues
show
Bug introduced by
The method saveProperties does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Product.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
579
                [
580
                    'Properties_Product_' . $newModel->id => $newModelProps,
581
                ]
582
            );
583
584
            // addons cloning
585
            foreach ($model->bindedAddons as $index => $addon) {
586
                $newModel->link(
0 ignored issues
show
Bug introduced by
The method link does only exist in app\modules\shop\models\Product, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
587
                    'bindedAddons',
588
                    $addon,
589
                    [
590
                        'sort_order'=>$index,
591
                        'appliance_object_id' => $object->id,
592
                    ]
593
                );
594
            }
595
596
            Yii::$app->session->setFlash('success', Yii::t('app', 'Product has been cloned successfully.'));
597
            $this->redirect(['edit', 'id' => $newModel->id, 'returnUrl' => $returnUrl]);
598
        }
599
    }
600
601
    /**
602
     * @param $id
603
     * @return \yii\web\Response
604
     * @throws NotFoundHttpException
605
     * @throws \Exception
606
     */
607
    public function actionDelete($id)
608
    {
609
        $product = Yii::$container->get(Product::class);
610
        /** @var Product $model */
611
        if (null === $model = $product::findOne($id)) {
612
            throw new NotFoundHttpException;
613
        }
614
615
        if (Yii::$app->request->get('returnUrl') !== null) {
616
            $redirect = Yii::$app->request->get('returnUrl');
617
        } elseif ($model->parent_id == 0) {
618
            $redirect = Url::toRoute(['index']);
619
        } else {
620
            $redirect = Url::toRoute(['edit', 'id' => $model->parent_id]);
621
        }
622
623
        if (!$model->delete()) {
624
            Yii::$app->session->setFlash('info', Yii::t('app', 'The object is placed in the cart'));
625
        } else {
626
            Yii::$app->session->setFlash('info', Yii::t('app', 'Object has been removed'));
627
        }
628
629
        return $this->redirect($redirect);
630
    }
631
632
    /**
633
     * @param $parent_id
634
     * @return \yii\web\Response
635
     * @throws \Exception
636
     */
637
    public function actionRemoveAll($parent_id)
638
    {
639
        $items = Yii::$app->request->post('items', []);
640
        if (!empty($items)) {
641
            $product = Yii::$container->get(Product::class);
642
            $items = $product::find()->where(['in', 'id', $items])->all();
643
            foreach ($items as $item) {
644
                $item->delete();
645
            }
646
        }
647
648
        return $this->redirect(['index', 'parent_id' => $parent_id]);
649
    }
650
651
    /**
652
     * @param $tableName
653
     * @param $ids
654
     * @param string $field
655
     * @return bool
656
     * @throws \yii\db\Exception
657
     */
658 View Code Duplication
    public static function sortModels($tableName, $ids, $field = 'sort_order')
659
    {
660
        $priorities = [];
661
        $start = 0;
662
        $ids_sorted = $ids;
663
        sort($ids_sorted);
664
        foreach ($ids as $id) {
665
            $priorities[$id] = $ids_sorted[$start++];
666
        }
667
        $sql = "UPDATE " . $tableName . " SET $field = " . self::generateCase($priorities) . " WHERE id IN(" . implode(
668
                ', ',
669
                $ids
670
            ) . ")";
671
672
        return Yii::$app->db->createCommand(
673
            $sql
674
        )->execute() > 0;
675
676
    }
677
678
    /**
679
     * Рекурсивный генератор свойств для создания комплектаций.
680
     * @param array $array Трехмерный массив вида:
681
     * [[['{property1}' => '{value1}']], [['{property1}' => '{value2}']], [['{property2}' => '{value1}']], [['{property2}' => '{value1}']]]
682
     * @param array $result Используется для передачи результатов внутри рекурсии
683
     * @param integer $count Счетчик внутри рекурсии
684
     * @return array
685
     */
686
    public static function generateOptions($array, $result = [], $count = 0)
687
    {
688
        if (empty($result)) {
689
            foreach ($array[$count] as $value) {
690
                $result[] = [$value];
691
            }
692
            $count++;
693
            $arResult = self::generateOptions($array, $result, $count);
694
        } else {
695
            if (isset($array[$count])) {
696
                $nextResult = [];
697
                foreach ($array[$count] as $value) {
698
                    foreach ($result as $resValue) {
699
                        $nextResult[] = array_merge($resValue, [$value]);
700
                    }
701
                }
702
                $count++;
703
                $arResult = self::generateOptions($array, $nextResult, $count);
704
            } else {
705
                return $result;
706
            }
707
        }
708
        return $arResult;
709
    }
710
711
    /**
712
     * @param $priorities
713
     * @return string
714
     */
715 View Code Duplication
    private static function generateCase($priorities)
716
    {
717
        $result = 'CASE `id`';
718
        foreach ($priorities as $k => $v) {
719
            $result .= ' when "' . $k . '" then "' . $v . '"';
720
        }
721
        return $result . ' END';
722
    }
723
724
    /**
725
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,false|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
726
     */
727
    public function actionAjaxRelatedProduct()
728
    {
729
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
730
731
        $result = [
732
            'more' => false,
733
            'results' => []
734
        ];
735
        $search = Yii::$app->request->get('search');
736
        if (!empty($search['term'])) {
737
            $query = new \yii\db\Query();
738
            $product = Yii::$container->get(Product::class);
739
            $query->select('id, name AS text')->from($product::tableName())->andWhere(
740
                ['like', 'name', $search['term']]
741
            )->andWhere(['active' => 1])->orderBy(['sort_order' => SORT_ASC, 'name' => SORT_ASC]);
742
            $command = $query->createCommand();
743
            $data = $command->queryAll();
744
745
            $result['results'] = array_values($data);
746
        }
747
748
        return $result;
749
    }
750
}