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.

Product   F
last analyzed

Complexity

Total Complexity 146

Size/Duplication

Total Lines 1126
Duplicated Lines 10.21 %

Coupling/Cohesion

Components 1
Dependencies 33

Importance

Changes 0
Metric Value
wmc 146
lcom 1
cbo 33
dl 115
loc 1126
rs 1.096
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A tableName() 0 4 1
A rules() 0 51 1
A attributeLabels() 0 32 1
A behaviors() 32 32 1
A search() 0 25 2
B findById() 32 35 6
B findBySlug() 0 49 7
A getCategory() 0 4 1
A getOptions() 0 4 1
A getCurrency() 0 4 1
A getRelatedProducts() 0 16 1
A getImage() 0 6 1
A getWarehousesState() 0 24 2
A beforeSave() 0 24 4
A afterSave() 0 11 1
A beforeDelete() 10 10 3
B saveCategoriesBindings() 7 40 5
A getCategoryIds() 0 11 2
C processImportBeforeSave() 5 34 11
C processImportAfterSave() 7 39 11
F unpackCategories() 21 78 24
A exportableAdditionalFields() 0 26 1
B getAdditionalFields() 0 41 10
F filteredProducts() 0 180 20
A getMainCategory() 0 4 1
A loadRelatedProductsArray() 0 7 2
A saveRelatedProducts() 0 22 3
A convertedPrice() 0 14 4
A formattedPrice() 0 31 3
A nativeCurrencyPrice() 0 5 1
A getMeasure() 0 11 3
B linkToCategory() 0 35 7
A getCacheTags() 0 16 2
A __toString() 0 4 1
A jsonSerialize() 0 4 1

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

1
<?php
2
3
namespace app\modules\shop\models;
4
5
use app\behaviors\CleanRelations;
6
use app\behaviors\Tree;
7
use app\components\Helper;
8
use app\modules\image\models\Image;
9
use app\models\BaseObject;
10
use app\modules\data\components\ImportableInterface;
11
use app\modules\data\components\ExportableInterface;
12
use app\modules\shop\data\FilterPagination;
13
use app\modules\shop\traits\HasAddonTrait;
14
use app\modules\shop\ShopModule;
15
use app\properties\HasProperties;
16
use app\properties\PropertiesHelper;
17
use app\traits\GetImages;
18
use devgroup\TagDependencyHelper\ActiveRecordHelper;
19
use Yii;
20
use yii\base\Exception;
21
use yii\behaviors\TimestampBehavior;
22
use yii\caching\TagDependency;
23
use yii\data\ActiveDataProvider;
24
use yii\db\ActiveQuery;
25
use yii\db\ActiveRecord;
26
use yii\db\Expression;
27
use yii\db\Query;
28
use yii\helpers\ArrayHelper;
29
use yii\data\Pagination;
30
use yii\web\ServerErrorHttpException;
31
32
/**
33
 * This is the model class for table "product".
34
 *
35
 * @property integer $id
36
 * @property integer $main_category_id
37
 * @property string $name
38
 * @property string $title
39
 * @property string $h1
40
 * @property string $meta_description
41
 * @property string $breadcrumbs_label
42
 * @property string $slug
43
 * @property string $slug_compiled
44
 * @property integer $slug_absolute
45
 * @property string $content
46
 * @property string $announce
47
 * @property integer $sort_order
48
 * @property integer $active
49
 * @property double $price
50
 * @property double $old_price
51
 * @property integer $parent_id
52
 * @property integer $currency_id
53
 * @property integer $measure_id
54
 * @property Product[] $relatedProducts
55
 * @property Measure $measure
56
 * @property string $sku
57
 * @property boolean $unlimited_count
58
 * @property string $date_added
59
 * @property string $date_modified
60
 * Relations:
61
 * @property Category $category
62
 * @property Product[] $children
63
 * @property Category $mainCategory
64
 */
65
66
class Product extends ActiveRecord implements ImportableInterface, ExportableInterface, \JsonSerializable
67
{
68
    use GetImages;
69
    use HasAddonTrait;
70
71
    protected static $identity_map = [];
72
    protected static $slug_to_id = [];
73
    protected $category_ids = null;
74
75
    public $relatedProductsArray = [];
76
77
    /**
78
     * @var null|WarehouseProduct[] Stores warehouses state of product. Use Product::getWarehousesState() to retrieve
79
     */
80
    protected $activeWarehousesState = null;
81
82
    /**
83
     * @inheritdoc
84
     */
85
    public static function tableName()
86
    {
87
        return '{{%product}}';
88
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93
    public function rules()
94
    {
95
        return [
96
            [['sku'], 'filter', 'filter' => ['\app\backend\components\Helper', 'toString']],
97
            [['main_category_id', 'name', 'slug'], 'required'],
98
            [
99
                [
100
                    'main_category_id',
101
                    'slug_absolute',
102
                    'sort_order',
103
                    'parent_id',
104
                    'currency_id',
105
                ],
106
                'integer'
107
            ],
108
            [
109
                [
110
                    'name',
111
                    'title',
112
                    'h1',
113
                    'meta_description',
114
                    'breadcrumbs_label',
115
                    'content',
116
                    'announce',
117
                    'option_generate',
118
                    'sku'
119
                ],
120
                'string'
121
            ],
122
            [
123
                [
124
                    'unlimited_count',
125
                    'active',
126
                    'slug_absolute',
127
                ],
128
                'boolean',
129
            ],
130
            [['price', 'old_price'], 'number'],
131
            [['slug'], 'string', 'max' => 80],
132
            [['slug_compiled'], 'string', 'max' => 180],
133
            [['old_price', 'price',], 'default', 'value' => 0,],
134
            [['active', 'unlimited_count'], 'default', 'value' => true],
135
            [['parent_id', 'slug_absolute', 'sort_order'], 'default', 'value' => 0],
136
            [['sku', 'name'], 'default', 'value' => ''],
137
            [['unlimited_count', 'currency_id', 'measure_id'], 'default', 'value' => 1],
138
            [['relatedProductsArray'], 'safe'],
139
            [['slug'], 'unique', 'targetAttribute' => ['slug', 'main_category_id']],
140
            [['date_added', 'date_modified'], 'safe'],
141
142
        ];
143
    }
144
    /**
145
     * @inheritdoc
146
     */
147
    public function attributeLabels()
148
    {
149
        return [
150
            'id' => Yii::t('app', 'ID'),
151
            'main_category_id' => Yii::t('app', 'Main Category ID'),
152
            'parent_id' => Yii::t('app', 'Parent ID'),
153
            'name' => Yii::t('app', 'Name'),
154
            'title' => Yii::t('app', 'Title'),
155
            'h1' => Yii::t('app', 'H1'),
156
            'meta_description' => Yii::t('app', 'Meta Description'),
157
            'breadcrumbs_label' => Yii::t('app', 'Breadcrumbs Label'),
158
            'slug' => Yii::t('app', 'Slug'),
159
            'slug_compiled' => Yii::t('app', 'Slug Compiled'),
160
            'slug_absolute' => Yii::t('app', 'Slug Absolute'),
161
            'content' => Yii::t('app', 'Content'),
162
            'announce' => Yii::t('app', 'Announce'),
163
            'sort_order' => Yii::t('app', 'Sort Order'),
164
            'active' => Yii::t('app', 'Active'),
165
            'price' => Yii::t('app', 'Price'),
166
            'old_price' => Yii::t('app', 'Old Price'),
167
            'option_generate' => Yii::t('app', 'Option Generate'),
168
            'in_warehouse' => Yii::t('app', 'Items in warehouse'),
169
            'sku' => Yii::t('app', 'SKU'),
170
            'unlimited_count' => Yii::t('app', 'Unlimited items(don\'t count in warehouse)'),
171
            'reserved_count' => Yii::t('app', 'Items reserved'),
172
            'relatedProductsArray' => Yii::t('app', 'Related products'),
173
            'currency_id' => Yii::t('app', 'Currency'),
174
            'measure_id' => Yii::t('app', 'Measure'),
175
            'date_added' => Yii::t('app', 'Date Added'),
176
            'date_modified' => Yii::t('app', 'Date Modified'),
177
        ];
178
    }
179
180
    /**
181
     * @inheritdoc
182
     */
183 View Code Duplication
    public function behaviors()
184
    {
185
        return [
186
            [
187
                'class' => Tree::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...
188
                'activeAttribute' => 'active',
189
                'sortOrder' => [
190
                    'sort_order' => SORT_ASC,
191
                    'id' => SORT_ASC
192
                ],
193
            ],
194
            [
195
                'class' => HasProperties::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...
196
            ],
197
            [
198
                'class' => ActiveRecordHelper::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...
199
            ],
200
            [
201
                'class' => CleanRelations::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...
202
            ],
203
            [
204
                'class' => TimestampBehavior::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...
205
                'createdAtAttribute' => 'date_added',
206
                'updatedAtAttribute' => 'date_modified',
207
                'value' => new Expression('NOW()'),
208
                'attributes' => [
209
                    ActiveRecord::EVENT_BEFORE_INSERT => ['date_added'],
210
                    ActiveRecord::EVENT_BEFORE_UPDATE => ['date_modified'],
211
                ],
212
            ],
213
        ];
214
    }
215
216
    /**
217
     * Search products
218
     * @param $params
219
     * @return ActiveDataProvider
220
     */
221
    public function search($params)
222
    {
223
        $product = Yii::$container->get(Product::class);
224
        /* @var $query \yii\db\ActiveQuery */
225
        $query = $product::find()->where(['parent_id' => 0])->with('images');
226
        $dataProvider = new ActiveDataProvider(
227
            [
228
                'query' => $query,
229
                'pagination' => [
230
                    'pageSize' => 10,
231
                ],
232
            ]
233
        );
234
        if (!($this->load($params))) {
235
            return $dataProvider;
236
        }
237
        $query->andFilterWhere(['id' => $this->id]);
238
        $query->andFilterWhere(['like', 'name', $this->name]);
239
        $query->andFilterWhere(['like', 'slug', $this->slug]);
240
        $query->andFilterWhere(['active' => $this->active]);
241
        $query->andFilterWhere(['price' => $this->price]);
242
        $query->andFilterWhere(['old_price' => $this->old_price]);
243
        $query->andFilterWhere(['like', 'sku', $this->sku]);
244
        return $dataProvider;
245
    }
246
247
    /**
248
     * Returns model instance by ID using per-request Identity Map and cache
249
     * @param $id
250
     * @param int $isActive Return only active
251
     * @return Product
252
     */
253 View Code Duplication
    public static function findById($id, $isActive = 1)
254
    {
255
        if (!is_numeric($id)) {
256
            return null;
257
        }
258
        if (!isset(static::$identity_map[$id])) {
259
            $cacheKey = static::tableName() . ":$id:$isActive";
260
            if (false === $model = Yii::$app->cache->get($cacheKey)) {
261
                $model = static::find()->where(['id' => $id])->with('images');
262
                if (null !== $isActive) {
263
                    $model->andWhere(['active' => $isActive]);
264
                }
265
                if (null !== $model = $model->one()) {
266
                    /**
267
                     * @var self $model
268
                     */
269
                    static::$slug_to_id[$model->slug] = $id;
270
                    Yii::$app->cache->set(
271
                        $cacheKey,
272
                        $model,
273
                        86400,
274
                        new TagDependency(
275
                            [
276
                                'tags' => [
277
                                    ActiveRecordHelper::getCommonTag(static::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...
278
                                ]
279
                            ]
280
                        )
281
                    );
282
                }
283
            }
284
            static::$identity_map[$id] = $model;
285
        }
286
        return static::$identity_map[$id];
287
    }
288
289
    /**
290
     * Find a product by slug
291
     * @param string $slug
292
     * @param int $inCategoryId
0 ignored issues
show
Documentation introduced by
Should the type for parameter $inCategoryId not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
293
     * @param int $isActive
294
     * @return Product
295
     */
296
    public static function findBySlug($slug, $inCategoryId = null, $isActive = 1)
297
    {
298
        if (!isset(static::$slug_to_id[$slug])) {
299
            $cacheKey = static::tableName() . "$slug:$inCategoryId";
300
            if (false === $model = Yii::$app->cache->get($cacheKey)) {
301
                $tags = [];
302
                /** @var ActiveQuery $model */
303
                $query = static::find()->where(
304
                    [
305
                        'slug' => $slug,
306
                        'active' => $isActive,
307
                    ]
308
                )->with('images', 'relatedProducts');
309
                if (!is_null($inCategoryId)) {
310
                    $query->andWhere(['main_category_id' => $inCategoryId]);
311
                    $tags[] = ActiveRecordHelper::getObjectTag(Category::className(), $inCategoryId);
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...
312
                }
313
                $model = $query->one();
314
                $product = Yii::$container->get(Product::class);
315
                /**
316
                 * @var self|null $model
317
                 */
318
                if (is_null($model)) {
319
                    $tags[] = ActiveRecordHelper::getCommonTag(get_class($product));
320
                } else {
321
                    $tags[] = ActiveRecordHelper::getObjectTag(get_class($product), $model->id);
322
                }
323
                Yii::$app->cache->set(
324
                    $cacheKey,
325
                    $model,
326
                    86400,
327
                    new TagDependency([
328
                        'tags' => $tags,
329
                    ])
330
                );
331
            }
332
            if (is_object($model)) {
333
                static::$identity_map[$model->id] = $model;
334
                static::$slug_to_id[$slug] = $model->id;
335
                return $model;
336
            }
337
            return null;
338
        } else {
339
            if (isset(static::$identity_map[static::$slug_to_id[$slug]])) {
340
                return static::$identity_map[static::$slug_to_id[$slug]];
341
            }
342
            return static::findById(static::$slug_to_id[$slug]);
343
        }
344
    }
345
346
    /**
347
     * @return Category|null
0 ignored issues
show
Documentation introduced by
Should the return type not be ActiveQuery?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
348
     */
349
    public function getCategory()
350
    {
351
        return $this->hasOne(Category::className(), ['id' => 'main_category_id']);
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...
352
    }
353
354
    public function getOptions()
355
    {
356
        return $this->hasMany(static::className(), ['parent_id' => 'id']);
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...
357
    }
358
359
    public function getCurrency()
360
    {
361
        return $this->hasOne(Currency::className(), ['id' => 'currency_id']);
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...
362
    }
363
364
    /**
365
     * @return ActiveQuery
366
     */
367
    public function getRelatedProducts()
368
    {
369
        $product = Yii::$container->get(Product::class);
370
        return $this->hasMany(get_class($product), ['id' => 'related_product_id'])
371
            ->viaTable(
372
                RelatedProduct::tableName(),
373
                ['product_id' => 'id'],
374
                function($relation) {
375
                    /** @var \yii\db\ActiveQuery $relation */
376
                    return $relation->orderBy('sort_order ASC');
377
                }
378
            )
379
            ->innerJoin('{{%related_product}}','related_product.product_id=:id AND related_product.related_product_id =product.id',[':id'=>$this->id])
380
            ->orderBy('related_product.sort_order ASC')
381
            ->andWhere(["active" => 1]);
382
    }
383
384
    public function getImage()
385
    {
386
        $result = $this->hasOne(Image::className(), ['object_model_id' => 'id']);
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...
387
        $object = BaseObject::getForClass($this->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...
388
        return $result->andWhere(['object_id' => $object->id]);
389
    }
390
391
392
    /**
393
     * Returns remains of this product in all active warehouses.
394
     * Note that if warehouse was added after product edit - it will not be shown here.
395
     * @return WarehouseProduct[]
396
     */
397
    public function getWarehousesState()
398
    {
399
        if ($this->activeWarehousesState === null) {
400
            $this->activeWarehousesState = WarehouseProduct::getDb()->cache(
401
                function($db) {
0 ignored issues
show
Unused Code introduced by
The parameter $db 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...
402
                    return WarehouseProduct::find()
403
                        ->where(['in', 'warehouse_id', Warehouse::activeWarehousesIds()])
404
                        ->andWhere('product_id=:product_id', [':product_id'=>$this->id])
405
                        ->with('warehouse')
406
                        ->all();
407
                },
408
                86400,
409
                new TagDependency(
410
                    [
411
                        'tags' => [
412
                            ActiveRecordHelper::getObjectTag($this->className(), $this->id),
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...
413
                        ]
414
                    ]
415
                )
416
            );
417
        }
418
419
        return $this->activeWarehousesState;
420
    }
421
422
    public function beforeSave($insert)
423
    {
424
        if (empty($this->breadcrumbs_label)) {
425
            $this->breadcrumbs_label = $this->name;
426
        }
427
428
        if (empty($this->h1)) {
429
            $this->h1 = $this->name;
430
        }
431
432
        if (empty($this->title)) {
433
            $this->title = $this->name;
434
        }
435
        $object = BaseObject::getForClass(static::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...
436
437
        TagDependency::invalidate(
438
            Yii::$app->cache,
439
            [
440
                'Images:' . $object->id . ':' . $this->id
441
            ]
442
        );
443
444
        return parent::beforeSave($insert);
445
    }
446
447
    /**
448
     * @inheritdoc
449
     */
450
    public function afterSave($insert, $changedAttributes)
451
    {
452
        parent::afterSave($insert, $changedAttributes);
453
        /**
454
         * @todo Implement a good decision instead of this.
455
         * It resolves a problem with Import (We keep data but do not use it. It produce a memory leak).
456
         * Another decision is do not save data if it does not exist (if (isset(static::$identity_map[$this->id]) === true) {static::$identity_map[$this->id] = $this;}).
457
         * Both decisions is not good. We think about it all day and all night but have no result. You may offer a true way :)
458
         */
459
        unset(static::$identity_map[$this->id]);
460
    }
461
462
    /**
463
     * Preparation to delete product.
464
     * Deleting all inserted products.
465
     * @return bool
466
     */
467 View Code Duplication
    public function beforeDelete()
468
    {
469
        if (!parent::beforeDelete()) {
470
            return false;
471
        }
472
        foreach ($this->children as $child) {
473
            $child->delete();
474
        }
475
        return true;
476
    }
477
478
    public function saveCategoriesBindings(array $categories_ids)
479
    {
480
        $object = BaseObject::getForClass(static::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...
481
        $catIds = $this->getCategoryIds();
482
483
        $remove = [];
484
        $add = [];
485
486
        foreach ($catIds as $catsKey => $value) {
487
            $key = array_search($value, $categories_ids);
488
            if ($key === false) {
489
                $remove[] = $value;
490
                unset($this->category_ids[$catsKey]);
491
            } else {
492
                unset($categories_ids[$key]);
493
            }
494
        }
495
        foreach ($categories_ids as $value) {
496
            $add[] = [
497
                $value,
498
                $this->id
499
            ];
500
            $this->category_ids[] = $value;
501
        }
502
503
        Yii::$app->db->createCommand()->delete(
504
            $object->categories_table_name,
505
            ['and', 'object_model_id = :id', ['in', 'category_id', $remove]],
506
            [':id' => $this->id]
507
        )->execute();
508 View Code Duplication
        if (!empty($add)) {
509
            Yii::$app->db->createCommand()->batchInsert(
510
                $object->categories_table_name,
511
                ['category_id', 'object_model_id'],
512
                $add
513
            )->execute();
514
        }
515
516
517
    }
518
519
    /**
520
     * @return array
521
     */
522
    public function getCategoryIds()
523
    {
524
        if ($this->category_ids === null) {
525
            $object = BaseObject::getForClass(static::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...
526
            $this->category_ids = (new Query())->select('category_id')->from([$object->categories_table_name])->where(
527
                'object_model_id = :id',
528
                [':id' => $this->id]
529
            )->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])->column();
530
        }
531
        return $this->category_ids;
532
    }
533
534
    /**
535
     * Process fields before the actual model is saved(inserted or updated)
536
     * @param array $fields
537
     * @param $multipleValuesDelimiter
538
     * @param array $additionalFields
539
     */
540
    public function processImportBeforeSave(array $fields, $multipleValuesDelimiter, array $additionalFields)
541
    {
542
        $_attributes = $this->attributes();
543
        foreach ($fields as $key => $value) {
544
            if (in_array($key, $_attributes)) {
545
                $this->$key = $value;
546
            }
547
        }
548
549
        $categories = $this->unpackCategories($fields, $multipleValuesDelimiter, $additionalFields);
550
        if ($categories !== false && $this->main_category_id < 1) {
551
            if (count($categories) == 0) {
552
                $categories = [1];
553
            }
554
            $this->main_category_id = $categories[0];
555
        }
556
557 View Code Duplication
        if (empty($this->slug)) {
558
            $this->slug = Helper::createSlug($this->name);
559
        } elseif (mb_strlen($this->slug) > 80) {
560
            $this->slug = mb_substr($this->slug, 0, 80);
561
        }
562
563
        if (empty($this->name)) {
564
            $this->name = 'unnamed-product';
565
        }
566
567
        if (!is_numeric($this->price)) {
568
            $this->price = 0;
569
        }
570
        if (!is_numeric($this->old_price)) {
571
            $this->old_price = 0;
572
        }
573
    }
574
575
    /**
576
     * Process fields after the actual model is saved(inserted or updated)
577
     * @param array $fields
578
     * @param $multipleValuesDelimiter
579
     * @param array $additionalFields
580
     */
581
    public function processImportAfterSave(array $fields, $multipleValuesDelimiter, array $additionalFields)
582
    {
583
        $categories = $this->unpackCategories($fields, $multipleValuesDelimiter, $additionalFields);
584
585
        if ($categories === false) {
586
            $categories = [$this->main_category_id];
587
        }
588
        $this->saveCategoriesBindings($categories);
0 ignored issues
show
Bug introduced by
It seems like $categories defined by $this->unpackCategories(...ter, $additionalFields) on line 583 can also be of type boolean; however, app\modules\shop\models\...aveCategoriesBindings() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
589
590
591
        $images = isset($fields['images']) ? $fields['images'] : (isset($fields['image']) ? $fields['image'] : false);
592
        if ($images !== false) {
593 View Code Duplication
            if (strpos($images, $multipleValuesDelimiter) > 0) {
594
                $images = explode($multipleValuesDelimiter, $images);
595
            } elseif (strpos($multipleValuesDelimiter, '/') === 0) {
596
                $images = preg_split($multipleValuesDelimiter, $images);
597
            } else {
598
                $images = [$images];
599
            }
600
            $input_array = [];
601
            foreach ($images as $image_src) {
602
                $input_array[] = [
603
                    'filename' => $image_src,
604
                ];
605
            }
606
            if (count($input_array) > 0) {
607
                Image::replaceForModel($this, $input_array);
608
            }
609
        }
610
611
        if (isset($additionalFields['relatedProducts'])) {
612
            if (isset($additionalFields['relatedProducts']['processValuesAs'],$fields['relatedProducts'])) {
613
614
                $ids = explode($multipleValuesDelimiter, $fields['relatedProducts']);
615
                $this->relatedProductsArray=$ids;
616
                $this->saveRelatedProducts();
617
            }
618
        }
619
    }
620
621
    /**
622
     * Makes an array of category ids from string
623
     *
624
     * @param array $fields
625
     * @param $multipleValuesDelimiter
626
     * @param array $additionalFields
627
     * @return array|bool
628
     */
629
    protected function unpackCategories(array $fields, $multipleValuesDelimiter, array $additionalFields)
630
    {
631
        $categories = isset($fields['categories']) ? $fields['categories'] : (isset($fields['category']) ? $fields['category'] : false);
632
        if ($categories === false || empty($fields['categories'])) {
633
            return $this->getCategoryIds();
634
635
        }
636
        if ($categories !== false) {
637 View Code Duplication
            if (strpos($categories, $multipleValuesDelimiter) > 0) {
638
                $categories = explode($multipleValuesDelimiter, $categories);
639
            } elseif (strpos($multipleValuesDelimiter, '/') === 0) {
640
                $categories = preg_split($multipleValuesDelimiter, $categories);
641
            } else {
642
                $categories = [$categories];
643
            }
644
            $typecast = 'id';
645
646
            if (isset($additionalFields['categories'])) {
647
                if (isset($additionalFields['categories']['processValuesAs'])) {
648
                    $typecast = $additionalFields['categories']['processValuesAs'];
649
                }
650
            }
651
            if ($typecast === 'id') {
652
                $categories = array_map('intval', $categories);
653
            } elseif ($typecast === 'slug') {
654
                $categories = array_map('trim', $categories);
655
                $categoryIds = [];
656 View Code Duplication
                foreach ($categories as $part) {
657
                    $cat = Category::findBySlug($part, 1, -1);
658
                    if (is_object($cat)) {
659
                        $categoryIds[] = $cat->id;
660
                    }
661
                    unset($cat);
662
                }
663
                $categories = array_map('intval', $categoryIds);
664
            } elseif ($typecast === 'name') {
665
                $categories = array_map('trim', $categories);
666
                $categoryIds = [];
667 View Code Duplication
                foreach ($categories as $part) {
668
                    $cat = Category::findByName($part, 1, -1);
669
                    if (is_object($cat)) {
670
                        $categoryIds[] = $cat->id;
671
                    }
672
                    unset($cat);
673
                }
674
                $categories = array_map('intval', $categoryIds);
675
            } else {
676
                // that's unusual behavior
677
                $categories = false;
678
            }
679
680
            // post-process categories
681
            // find & add parent category
682
            // if we need to show products of child categories in products list
683
            /** @var ShopModule $module */
684
            $module = Yii::$app->getModule('shop');
685
            if (is_array($categories) && $module->showProductsOfChildCategories) {
686
                do {
687
                    $repeat = false;
688
                    foreach ($categories as $cat) {
689
                        $model = Category::findById($cat, null, null);
690
                        if ($model === null) {
691
                            echo "\n\nUnknown category with id ".intval($cat) ." for model with id:".$this->id."\n\n";
692
                            continue;
693
                        }
694
                        if (intval($model->parent_id) > 0 && in_array($model->parent_id, $categories) === false) {
695
                            $categories[] = $model->parent_id;
696
                            $repeat = true;
697
                        }
698
699
                        unset($model);
700
                    }
701
                } while ($repeat === true);
702
            }
703
704
        }
705
        return $categories;
706
    }
707
708
    /**
709
     * Additional fields with labels.
710
     * Translation should be implemented internally in this function.
711
     * For now will be rendered as checkbox list with label.
712
     * Note: properties should not be in the result - they are served other way.
713
     * Format:
714
     * [
715
     *      'field_key' => 'Your awesome translated field title',
716
     *      'another' => 'Another field label',
717
     * ]
718
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<strin...|array<string,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...
719
     */
720
    public static function exportableAdditionalFields()
721
    {
722
        return [
723
            'categories' => [
724
                'label' => Yii::t('app', 'Categories'),
725
                'processValueAs' => [
726
                    'id' => Yii::t('app', 'ID'),
727
                    'name' => Yii::t('app', 'Name'),
728
                    'slug' => Yii::t('app', 'Slug'),
729
                ]
730
            ],
731
            'images' => [
732
                'label' => Yii::t('app', 'Images'),
733
                'processValueAs' => [
734
                    'filename' => Yii::t('app', 'Filename'),
735
                    'id' => Yii::t('app', 'ID'),
736
                ]
737
            ],
738
            'relatedProducts' => [
739
                'label' => Yii::t('app', 'Related products'),
740
                'processValueAs' => [
741
                    'id' => Yii::t('app', 'ID'),
742
                ],
743
            ],
744
        ];
745
    }
746
747
    /**
748
     * Returns additional fields data by field key.
749
     * If value of field is array it will be converted to string
750
     * using multipleValuesDelimiter specified in ImportModel
751
     * @param array $configuration
752
     * @return array
753
     */
754
    public function getAdditionalFields(array $configuration)
755
    {
756
        $result = [];
757
758
        if (isset($configuration['categories'], $configuration['categories']['processValuesAs'])
759
            && $configuration['categories']['enabled']
760
        ) {
761
            if ($configuration['categories']['processValuesAs'] === 'id') {
762
                $result['categories'] = $this->getCategoryIds();
763
            } else {
764
                $ids = $this->getCategoryIds();
765
                $result['categories'] = [];
766
767
                foreach ($ids as $id) {
768
                    $category = Category::findById($id, null, null);
769
                    if ($category) {
770
                        $result['categories'][] = $category->getAttribute(
771
                            $configuration['categories']['processValuesAs']
772
                        );
773
                    }
774
                    unset($category);
775
                }
776
            }
777
        }
778
        if (isset($configuration['images'], $configuration['images']['processValuesAs'])
779
            && $configuration['images']['enabled']
780
        ) {
781
            $object = BaseObject::getForClass($this->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...
782
            $images = Image::getForModel($object->id, $this->id);
783
            $result['images'] = ArrayHelper::getColumn($images, $configuration['images']['processValuesAs']);
784
        }
785
786
        if (isset($configuration['relatedProducts'], $configuration['relatedProducts']['processValuesAs'])
787
            && $configuration['relatedProducts']['enabled']
788
        ) {
789
            $result['relatedProducts'] = ArrayHelper::getColumn($this->relatedProducts, $configuration['relatedProducts']['processValuesAs']);
790
        }
791
792
793
        return $result;
794
    }
795
796
    /**
797
     * Returns products for special filtration query
798
     * Used in ProductsWidget and ProductController
799
     *
800
     * @param $category_group_id
801
     * @param array $values_by_property_id
802
     * @param null|integer|string $selected_category_id
803
     * @param bool|string $force_sorting If false - use UserPreferences, if string - use supplied orderBy condition
804
     * @param null|integer $limit limit query results
805
     * @param bool $apply_filterquery Should we apply filter query(filters based on query params ie. price_min/max)
806
     * @param bool $force_limit False to use Pagination, true to use $limit and ignore pagination
807
     * @param array $additional_filters Array of callables that will apply additional filters to query
808
     * @return array
809
     * @throws ServerErrorHttpException
810
     * @throws \Exception
811
     */
812
    public static function filteredProducts(
813
        $category_group_id,
814
        array $values_by_property_id = [],
815
        $selected_category_id = null,
816
        $force_sorting = false,
817
        $limit = null,
818
        $apply_filterquery = true,
819
        $force_limit = false,
820
        array $additional_filters = []
821
    ) {
822
        Yii::beginProfile("FilteredProducts");
823
        /** @var \app\models\BaseObject $object */
824
        if (null === $object = BaseObject::getForClass(static::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...
825
            throw new ServerErrorHttpException('Object not found.');
826
        }
827
828
        /** @var \app\modules\shop\ShopModule $module */
829
        $module = Yii::$app->getModule('shop');
830
831
832
        $query = static::find()->with('images');
833
        if ($module->productsFilteringMode === ConfigConfigurationModel::FILTER_PARENTS_ONLY) {
834
            $query->andWhere([static::tableName() . '.parent_id' => 0]);
835
        } elseif ($module->productsFilteringMode === ConfigConfigurationModel::FILTER_CHILDREN_ONLY) {
836
            $query->andWhere(['!=', static::tableName() . '.parent_id', 0]);
837
        }
838
        $query->andWhere([static::tableName() . '.active' => 1]);
839
840
841
        if (null !== $selected_category_id) {
842
            $query->innerJoin(
843
                $object->categories_table_name . ' ocats',
844
                'ocats.category_id = :catid AND ocats.object_model_id = ' . static::tableName() . '.id',
845
                [':catid' => $selected_category_id]
846
            );
847
        } else {
848
            $query->innerJoin(
849
                $object->categories_table_name . ' ocats',
850
                'ocats.object_model_id = ' . static::tableName() . '.id'
851
            );
852
        }
853
854
        $query->innerJoin(
855
            Category::tableName() . ' ocatt',
856
            'ocatt.id = ocats.category_id AND ocatt.category_group_id = :gcatid AND ocatt.active = 1',
857
            [':gcatid' => $category_group_id]
858
        );
859
        $query->addGroupBy(static::tableName() . ".id");
860
861
862
        $userSelectedSortingId = UserPreferences::preferences()->getAttributes()['productListingSortId'];
863
        $allSorts = [];
864
        if ($force_sorting === false) {
865
            $allSorts = ProductListingSort::enabledSorts();
866
            if (isset($allSorts[$userSelectedSortingId])) {
867
                if ($allSorts[$userSelectedSortingId]['sort_field'] == 'product.price') {
868
                    $query->leftJoin(Currency::tableName() . ' currency_order ON currency_order.id = product.currency_id');
869
                    $query->addOrderBy(
870
                        'product.`price`*currency_order.`convert_rate`' . ' '
871
                        . $allSorts[$userSelectedSortingId]['asc_desc']
872
                    );
873
                } else {
874
                    $query->addOrderBy(
875
                        $allSorts[$userSelectedSortingId]['sort_field'] . ' '
876
                        . $allSorts[$userSelectedSortingId]['asc_desc']
877
                    );
878
                }
879
            } else {
880
                $query->addOrderBy(static::tableName() . '.sort_order');
881
            }
882
        } elseif (empty($force_sorting) === false || is_array($force_sorting) === true) {
883
            $query->addOrderBy($force_sorting);
884
        }
885
886
        $productsPerPage = $limit === null ? UserPreferences::preferences()->getAttributes(
887
        )['productsPerPage'] : $limit;
888
889
        PropertiesHelper::appendPropertiesFilters(
890
            $object,
891
            $query,
892
            $values_by_property_id,
893
            Yii::$app->request->get('p', []),
894
            $module->multiFilterMode
895
        );
896
897
898
        // apply additional filters
899
        $cacheKeyAppend = "";
900
        if ($apply_filterquery) {
901
            $query = Yii::$app->filterquery->filter($query, $cacheKeyAppend);
902
        }
903
904
        foreach ($additional_filters as $filter) {
905
            if (is_callable($filter)) {
906
                call_user_func_array(
907
                    $filter,
908
                    [
909
                        &$query,
910
                        &$cacheKeyAppend
911
                    ]
912
                );
913
            }
914
        }
915
916
        $cacheKey = 'ProductsCount:' . implode(
917
                '_',
918
                [
919
                    md5($query->createCommand()->getRawSql()),
920
                    $limit ? '1' : '0',
921
                    $force_limit ? '1' : '0',
922
                    $productsPerPage
923
                ]
924
            ) . $cacheKeyAppend;
925
926
927
        $pages = null;
928
929
        if ($force_limit === true) {
930
            $query->limit($limit);
931
        } else {
932
            $products_query = clone $query;
933
            $products_query->limit(null);
934
935
            if (false === $pages = Yii::$app->cache->get($cacheKey)) {
936
                $pages = new FilterPagination(
937
                    [
938
                        'defaultPageSize' => !is_null($query->limit) ? $query->limit : $productsPerPage,
939
                        'pageSizeLimit' => [],
940
                        'forcePageParam' => false,
941
                        'totalCount' => $products_query->count(),
942
                    ]
943
                );
944
945
                Yii::$app->cache->set(
946
                    $cacheKey,
947
                    $pages,
948
                    86400,
949
                    new TagDependency(
950
                        [
951
                            'tags' => [
952
                                ActiveRecordHelper::getCommonTag(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...
953
                                ActiveRecordHelper::getCommonTag(static::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...
954
                                ActiveRecordHelper::getCommonTag($module->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...
955
                            ]
956
                        ]
957
                    )
958
                );
959
            }
960
961
            $query->offset($pages->offset)->limit($pages->limit);
962
        }
963
964
        $cacheKey .= '-' . Yii::$app->request->get('page', 1);
965
966
        if (false === $products = Yii::$app->cache->get($cacheKey)) {
967
            $products = $query->all();
968
            Yii::$app->cache->set(
969
                $cacheKey,
970
                $products,
971
                86400,
972
                new TagDependency(
973
                    [
974
                        'tags' => [
975
                            ActiveRecordHelper::getCommonTag(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...
976
                            ActiveRecordHelper::getCommonTag(static::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...
977
                            ActiveRecordHelper::getCommonTag($module->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...
978
                        ]
979
                    ]
980
                )
981
            );
982
        }
983
984
        Yii::endProfile("FilteredProducts");
985
        return [
986
            'products' => $products,
987
            'pages' => $pages,
988
            'allSorts' => $allSorts,
989
        ];
990
991
    }
992
993
    /**
994
     * Returns product main category model instance using per-request Identity Map
995
     * @return Category|null
996
     */
997
    public function getMainCategory()
998
    {
999
        return Category::findById($this->main_category_id, null);
1000
    }
1001
1002
    public function loadRelatedProductsArray()
1003
    {
1004
        $this->relatedProductsArray = [];
1005
        foreach ($this->relatedProducts as $product) {
1006
            $this->relatedProductsArray[] = $product->id;
1007
        }
1008
    }
1009
1010
    public function saveRelatedProducts()
1011
    {
1012
        if (!is_array($this->relatedProductsArray)) {
1013
            $this->relatedProductsArray = explode(',', $this->relatedProductsArray);
1014
        }
1015
1016
        RelatedProduct::deleteAll(
1017
            [
1018
                'product_id' => $this->id,
1019
            ]
1020
        );
1021
1022
        foreach ($this->relatedProductsArray as $index => $relatedProductId) {
1023
            $relation = new RelatedProduct;
1024
            $relation->attributes = [
1025
                'product_id' => $this->id,
1026
                'related_product_id' => $relatedProductId,
1027
                'sort_order' => $index,
1028
            ];
1029
            $relation->save();
1030
        }
1031
    }
1032
1033
    /**
1034
     * Returns product price(old old_price) converted to $currency. If currency is not set(null) will be converted to main currency
1035
     * @param null|Currency $currency Currency to use in conversion, null for main shop currency
1036
     * @param bool $oldPrice True if you want to return Old Price instead of price
1037
     * @return float
1038
     */
1039
    public function convertedPrice($currency = null, $oldPrice = false)
1040
    {
1041
        if ($currency === null) {
1042
            $currency = Currency::getMainCurrency();
1043
        }
1044
        $price = $oldPrice === true ? $this->old_price : $this->price;
1045
1046
        if ($this->currency_id !== $currency->id) {
1047
            // we need to convert!
1048
            $foreignCurrency = Currency::findById($this->currency_id);
1049
            return round($price / $foreignCurrency->convert_nominal * $foreignCurrency->convert_rate, 2);
1050
        }
1051
        return $price;
1052
    }
1053
1054
    /**
1055
     * Formats price to needed currency
1056
     * @param null|Currency $currency Currency to use in conversion, null for main shop currency
1057
     * @param boolean $oldPrice True if you want to return Old Price instead of price
1058
     * @param boolean $schemaOrg Return schema.org http://schema.org/Offer markup with span's
1059
     * @return string
1060
     */
1061
    public function formattedPrice($currency = null, $oldPrice = false, $schemaOrg = true)
1062
    {
1063
        if ($currency === null) {
1064
            $currency = Currency::getMainCurrency();
1065
        }
1066
1067
        $price = $this->convertedPrice($currency, $oldPrice);
1068
1069
1070
        $formatted_string = $currency->format($price);
1071
        if ($schemaOrg == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1072
            return strtr(
1073
                '
1074
                <span itemtype="http://schema.org/Offer" itemprop="offers" itemscope>
1075
                    <meta itemprop="priceCurrency" content="%iso_code%">
1076
                    <span itemprop="price" content="%price%">
1077
                        %formatted_string%
1078
                    </span>
1079
                </span>
1080
                ',
1081
                [
1082
                    '%iso_code%' => $currency->iso_code,
1083
                    '%price%' => $price,
1084
                    '%formatted_string%' => $formatted_string,
1085
                ]
1086
            );
1087
        } else {
1088
            return $formatted_string;
1089
        }
1090
1091
    }
1092
1093
    /**
1094
     * Formats price in product's currency
1095
     * @param bool $oldPrice
1096
     * @param bool $schemaOrg
1097
     * @return string
1098
     */
1099
    public function nativeCurrencyPrice($oldPrice = false, $schemaOrg = true)
1100
    {
1101
        $currency = Currency::findById($this->currency_id);
1102
        return $this->formattedPrice($currency, $oldPrice, $schemaOrg);
1103
    }
1104
1105
    public function getMeasure()
1106
    {
1107
        $measure = Measure::findById($this->measure_id);
1108
        if (is_null($measure)) {
1109
            $measure = Measure::findById(Yii::$app->getModule('shop')->defaultMeasureId);
1110
        }
1111
        if (is_null($measure)) {
1112
            throw new Exception('Measure not found');
1113
        }
1114
        return $measure;
1115
    }
1116
1117
    /**
1118
     * @param int|Category|null $category
1119
     * @param bool $asMainCategory
1120
     * @return bool
1121
     * @throws \yii\db\Exception
1122
     */
1123
    public function linkToCategory($category = null, $asMainCategory = false)
1124
    {
1125
        if ($category instanceof Category) {
1126
            $category = $category->id;
1127
        } elseif (is_int($category) || is_string($category)) {
1128
            $category = intval($category);
1129
        } else {
1130
            return false;
1131
        }
1132
1133
        if ($asMainCategory) {
1134
            $this->main_category_id = $category;
1135
            return $this->save();
1136
        } else {
1137
            $tableName = $this->getObject()->categories_table_name;
1138
            $query = new Query();
1139
            $query = $query->select(['id'])
1140
                ->from($tableName)
1141
                ->where(['category_id' => $category, 'object_model_id' => $this->id])
1142
                ->one();
1143
            if (false === $query) {
1144
                try {
1145
                    $result = Yii::$app->db->createCommand()->insert($tableName, [
1146
                        'category_id' => $category,
1147
                        'object_model_id' => $this->id
1148
                    ])->execute();
1149
                } catch (\yii\db\Exception $e) {
1150
                    $result = false;
1151
                }
1152
                return boolval($result);
1153
            }
1154
        }
1155
1156
        return false;
1157
    }
1158
1159
    public function getCacheTags()
1160
    {
1161
        $product = Yii::$container->get(Product::class);
1162
        $tags = [
1163
            ActiveRecordHelper::getObjectTag(get_class($product), $this->id),
1164
        ];
1165
        $category = $this->getMainCategory();
1166
        $tags [] = ActiveRecordHelper::getObjectTag(Category::className(), $category->id);
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...
1167
        $categoryParentsIds = $category->getParentIds();
1168
        foreach ($categoryParentsIds as $id) {
1169
            $tags [] = ActiveRecordHelper::getObjectTag(Category::className(), $id);
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...
1170
        };
1171
1172
1173
        return $tags;
1174
    }
1175
1176
    /**
1177
     * @return string
1178
     */
1179
    public function __toString()
1180
    {
1181
        return ($this->className() . ':' . $this->id);
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...
1182
    }
1183
1184
    /**
1185
     * @return string
1186
     */
1187
    public function jsonSerialize()
1188
    {
1189
        return ($this->className() . ':' . $this->id);
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...
1190
    }
1191
}
1192