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.
Passed
Push — master ( 05ab1f...7cfc28 )
by Alexander
30s
created

Category::processImportAfterSave()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 3
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\models\Object;
9
use app\modules\data\components\ImportableInterface;
10
use app\modules\shop\models\FilterSets;
11
use app\properties\HasProperties;
12
use app\traits\GetImages;
13
use devgroup\TagDependencyHelper\ActiveRecordHelper;
14
use Yii;
15
use yii\behaviors\TimestampBehavior;
16
use yii\caching\TagDependency;
17
use yii\data\ActiveDataProvider;
18
use yii\db\ActiveRecord;
19
use yii\db\Expression;
20
use yii\helpers\ArrayHelper;
21
use yii\helpers\Url;
22
23
/**
24
 * This is the model class for table "category".
25
 *
26
 * @property integer $id
27
 * @property integer $category_group_id
28
 * @property integer $parent_id
29
 * @property string $name
30
 * @property string $title
31
 * @property string $h1
32
 * @property string $meta_description
33
 * @property string $breadcrumbs_label
34
 * @property string $slug
35
 * @property string $slug_compiled
36
 * @property integer $slug_absolute
37
 * @property string $content
38
 * @property string $announce
39
 * @property integer $sort_order
40
 * @property boolean $active
41
 * @property string $date_added
42
 * @property string $date_modified
43
 * @property Category[] $children
44
 * @property Category $parent
45
 */
46
class Category extends ActiveRecord implements \JsonSerializable, ImportableInterface
47
{
48
    use GetImages;
49
50
    const DELETE_MODE_SINGLE_CATEGORY = 1;
51
    const DELETE_MODE_ALL = 2;
52
    const DELETE_MODE_MAIN_CATEGORY = 3;
53
    const CATEGORY_PARENT = 0;
54
    const CATEGORY_ACTIVE = 1;
55
    const CATEGORY_INACTIVE = 0;
56
57
    /**
58
     * Category identity map
59
     * [
60
     *      'category_id' => $category_model_instance,
61
     * ]
62
     *
63
     * Used by findById
64
     * @var array
65
     */
66
    public static $identity_map = [];
67
68
    /**
69
     * Special caching for findBySlug
70
     * Stores category->id for pair slug:category_group_id:parent_id
71
     * @var array
72
     */
73
    private static $id_by_slug_group_parent = [];
74
    private static $id_by_name_group_parent = [];
75
76
    public $deleteMode = 1;
77
78
    /**
79
     * @var null|string Url path caching variable
80
     */
81
    private $urlPath = null;
82
83
    /** @var null|array Array of parent categories ids including root category */
84
    private $parentIds = null;
85
86
    /** @var FilterSets[] */
87
    private $filterSets = null;
88
89
    public function behaviors()
90
    {
91
        return [
92
            [
93
                'class' => HasProperties::className(),
94
            ],
95
            [
96
                'class' => \devgroup\TagDependencyHelper\ActiveRecordHelper::className(),
97
            ],
98
            [
99
                'class' => CleanRelations::className(),
100
            ],
101
            [
102
                'class' => Tree::className(),
103
                'activeAttribute' => 'active',
104
                'sortOrder' => [
105
                    'sort_order' => SORT_ASC,
106
                    'id' => SORT_ASC
107
                ],
108
            ],
109
            [
110
                'class' => TimestampBehavior::className(),
111
                'createdAtAttribute' => 'date_added',
112
                'updatedAtAttribute' => 'date_modified',
113
                'value' => new Expression('NOW()'),
114
                'attributes' => [
115
                    ActiveRecord::EVENT_BEFORE_INSERT => ['date_added'],
116
                    ActiveRecord::EVENT_BEFORE_UPDATE => ['date_modified'],
117
                ],
118
            ],
119
        ];
120
    }
121
122
    /**
123
     * Return delete modes list
124
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use 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...
125
     */
126 View Code Duplication
    public static function deleteModesList()
127
    {
128
        return [
129
            self::DELETE_MODE_MAIN_CATEGORY => Yii::t('app', 'Delete all products that relate to this category as main'),
130
            self::DELETE_MODE_SINGLE_CATEGORY => Yii::t('app', 'Delete only that products that exists ONLY in that category'),
131
            self::DELETE_MODE_ALL => Yii::t('app', 'Delete along with it no matter what'),
132
        ];
133
    }
134
135
    /**
136
     * @inheritdoc
137
     */
138
    public static function tableName()
139
    {
140
        return '{{%category}}';
141
    }
142
143
    /**
144
     * @inheritdoc
145
     */
146 View Code Duplication
    public function rules()
147
    {
148
        return [
149
            [['category_group_id', 'parent_id', 'name', 'slug'], 'required'],
150
            [['category_group_id', 'parent_id', 'slug_absolute', 'sort_order', 'active'], 'integer'],
151
            [['name', 'title', 'h1', 'meta_description', 'breadcrumbs_label', 'content', 'announce'], 'string'],
152
            [['slug'], 'string', 'max' => 80],
153
            [['slug_compiled'], 'string', 'max' => 180],
154
            [['title_append'], 'string'],
155
            [['active'], 'default', 'value' => 1],
156
            [['date_added', 'date_modified'], 'safe'],
157
            ['sort_order', 'default', 'value' => 0],
158
        ];
159
    }
160
161
    /**
162
     * @inheritdoc
163
     */
164
    public function beforeValidate()
165
    {
166
        if (empty($this->slug) && !empty($this->name)) {
167
            $this->slug = Helper::createSlug($this->name);
168
        }
169
        if (empty($this->title) && !empty($this->name)) {
170
            $this->title = $this->name;
171
        }
172
        if (empty($this->h1) && !empty($this->title)) {
173
            $this->h1 = $this->title;
174
        }
175
        if (empty($this->breadcrumbs_label) && !empty($this->name)) {
176
            $this->breadcrumbs_label = $this->name;
177
        }
178
        return parent::beforeValidate();
179
    }
180
181
    /**
182
     * @inheritdoc
183
     */
184
    public function attributeLabels()
185
    {
186
        return [
187
            'id' => Yii::t('app', 'ID'),
188
            'category_group_id' => Yii::t('app', 'Category Group ID'),
189
            'parent_id' => Yii::t('app', 'Parent ID'),
190
            'name' => Yii::t('app', 'Name'),
191
            'title' => Yii::t('app', 'Title'),
192
            'h1' => Yii::t('app', 'H1'),
193
            'meta_description' => Yii::t('app', 'Meta Description'),
194
            'breadcrumbs_label' => Yii::t('app', 'Breadcrumbs Label'),
195
            'slug' => Yii::t('app', 'Slug'),
196
            'slug_compiled' => Yii::t('app', 'Slug Compiled'),
197
            'slug_absolute' => Yii::t('app', 'Slug Absolute'),
198
            'content' => Yii::t('app', 'Content'),
199
            'announce' => Yii::t('app', 'Announce'),
200
            'sort_order' => Yii::t('app', 'Sort Order'),
201
            'active' => Yii::t('app', 'Active'),
202
            'title_append' => Yii::t('app', 'Title Append'),
203
            'date_added' => Yii::t('app', 'Date Added'),
204
            'date_modified' => Yii::t('app', 'Date Modified'),
205
        ];
206
    }
207
208
    /**
209
     * Search tasks
210
     * @param $params
211
     * @return ActiveDataProvider
212
     */
213
    public function search($params)
214
    {
215
        /* @var $query \yii\db\ActiveQuery */
216
        $query = self::find()->where(['parent_id' => $this->parent_id])->with('images');
217
        $dataProvider = new ActiveDataProvider(
218
            [
219
                'query' => $query,
220
                'pagination' => [
221
                    'pageSize' => 10,
222
                ],
223
            ]
224
        );
225
        if (!($this->load($params))) {
226
            return $dataProvider;
227
        }
228
        $query->andFilterWhere(['id' => $this->id]);
229
        $query->andFilterWhere(['like', 'name', $this->name]);
230
        $query->andFilterWhere(['like', 'slug', $this->slug]);
231
        $query->andFilterWhere(['like', 'slug_compiled', $this->slug_compiled]);
232
        $query->andFilterWhere(['like', 'slug_absolute', $this->slug_absolute]);
233
        $query->andFilterWhere(['like', 'title', $this->title]);
234
        $query->andFilterWhere(['like', 'h1', $this->h1]);
235
        $query->andFilterWhere(['like', 'meta_description', $this->meta_description]);
236
        $query->andFilterWhere(['like', 'breadcrumbs_label', $this->breadcrumbs_label]);
237
        $query->andFilterWhere(['active' => $this->active]);
238
        return $dataProvider;
239
    }
240
241
    /**
242
     * Returns model using indentity map and cache
243
     * @param int $id
244
     * @param int|null $isActive
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $isActive a bit more specific; maybe use integer.
Loading history...
245
     * @return Category|null
246
     */
247
    public static function findById($id, $isActive = 1)
248
    {
249
        if (!is_numeric($id)) {
250
            return null;
251
        }
252
        if (!isset(static::$identity_map[$id])) {
253
            $cacheKey = static::tableName() . ":$id:$isActive";
254
            if (false === $model = Yii::$app->cache->get($cacheKey)) {
255
                $model = static::find()->where(['id' => $id])->with('images');
256
                if (null !== $isActive) {
257
                    $model->andWhere(['active' => $isActive]);
258
                }
259
                if (null !== $model = $model->one()) {
260
                    Yii::$app->cache->set(
261
                        $cacheKey,
262
                        $model,
263
                        86400,
264
                        new TagDependency(
265
                            [
266
                                'tags' => [
267
                                    ActiveRecordHelper::getObjectTag($model, $model->id)
268
                                ]
269
                            ]
270
                        )
271
                    );
272
                }
273
            }
274
            static::$identity_map[$id] = $model;
275
        }
276
277
        return static::$identity_map[$id];
278
    }
279
280
    /**
281
     * Finds category by slug inside category_group_id
282
     * Uses cache and identity_map
283
     */
284
    public static function findBySlug($slug, $category_group_id, $parent_id = 0)
285
    {
286
        $params = [
287
            'slug' => $slug,
288
            'category_group_id' => $category_group_id,
289
        ];
290
        if ($parent_id >= 0) {
291
            $params['parent_id'] = $parent_id;
292
        }
293
294
        $identity_key = $slug . ':' . $category_group_id . ':' . $parent_id;
295
        if (isset(static::$id_by_slug_group_parent[$identity_key]) === true) {
296
            return static::findById(static::$id_by_slug_group_parent[$identity_key], null);
297
        }
298
        $category = Yii::$app->cache->get("Category:bySlug:" . $identity_key);
299
        if ($category === false) {
300
            $category = Category::find()->where($params)->one();
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
301
            if (is_object($category) === true) {
302
                static::$identity_map[$category->id] = $category;
303
                static::$id_by_slug_group_parent[$identity_key] = $category->id;
304
                Yii::$app->cache->set(
305
                    "Category:bySlug:" . $identity_key,
306
                    $category,
307
                    86400,
308
                    new TagDependency(
309
                        [
310
                            'tags' => ActiveRecordHelper::getObjectTag($category, $category->id)
311
                        ]
312
                    )
313
                );
314
                return $category;
315 View Code Duplication
            } else {
316
                Yii::$app->cache->set(
317
                    "Category:bySlug:" . $identity_key,
318
                    $category,
319
                    86400,
320
                    new TagDependency(
321
                        [
322
                            'tags' => ActiveRecordHelper::getCommonTag(Category::className())
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
323
                        ]
324
                    )
325
                );
326
                return null;
327
            }
328
        } else {
329
            return $category;
330
        }
331
    }
332
333
    /**
334
     * Ищет по name в рамках category_group_id
335
     * Заносит в identity_map
336
     */
337
    public static function findByName($name, $category_group_id, $parent_id = 0)
338
    {
339
        $params = [
340
            'name' => $name,
341
            'category_group_id' => $category_group_id,
342
        ];
343
        if ($parent_id >= 0) {
344
            $params['parent_id'] = $parent_id;
345
        }
346
347
        $identity_key = $name . ':' . $category_group_id . ':' . $parent_id;
348
        if (isset(static::$id_by_name_group_parent[$identity_key])) {
349
            return static::findById(static::$id_by_name_group_parent[$identity_key], null);
350
        }
351
        $category = Yii::$app->cache->get("Category:byName:" . $identity_key);
352
        if (!is_object($category)) {
353
            $category = Category::find()->where($params)->one();
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
354
            if (is_object($category)) {
355
                static::$identity_map[$category->id] = $category;
356
                static::$id_by_name_group_parent[$identity_key] = $category->id;
357
                Yii::$app->cache->set(
358
                    "Category:byName:" . $identity_key,
359
                    $category,
360
                    86400,
361
                    new TagDependency(
362
                        [
363
                            'tags' => ActiveRecordHelper::getObjectTag($category, $category->id)
364
                        ]
365
                    )
366
                );
367
                return $category;
368
            } else {
369
                return null;
370
            }
371
        } else {
372
            return $category;
373
        }
374
    }
375
376
    public function getUrlPath($include_parent_category = true)
377
    {
378
        if ($this->urlPath === null) {
379
            if ($this->parent_id == 0) {
380
                if ($include_parent_category) {
381
                    return $this->slug;
382
                } else {
383
                    return '';
384
                }
385
            }
386
            $slugs = [$this->slug];
387
388
            $this->parentIds = [];
389
390
            $parent_category = $this->parent_id > 0 ? $this->parent : 0;
391 View Code Duplication
            while ($parent_category !== null) {
392
                $this->parentIds[] = intval($parent_category->id);
393
                $slugs[] = $parent_category->slug;
394
                $parent_category = $parent_category->parent;
395
            }
396
            if ($include_parent_category === false) {
397
                array_pop($slugs);
398
            }
399
            $this->urlPath = implode("/", array_reverse($slugs));
400
        }
401
        return $this->urlPath;
402
    }
403
404
    /**
405
     * @return array Returns array of ids of parent categories(breadcrumbs ids)
406
     */
407
    public function getParentIds()
408
    {
409
        if ($this->parentIds === null) {
410
            $this->parentIds = [];
411
            $parent_category = $this->parent_id > 0 ? $this->parent : null;
412 View Code Duplication
            while ($parent_category !== null) {
413
                $this->parentIds[] = intval($parent_category->id);
414
                $parent_category = $parent_category->parent;
415
            }
416
        }
417
        return $this->parentIds;
418
    }
419
420
    /**
421
     * Ищет root для группы категорий
422
     * Использует identity_map
423
     * @return Category|null
0 ignored issues
show
Documentation introduced by
Should the return type not be null|ActiveRecord|array? Also, consider making the array more specific, something like array<String>, or String[].

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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
424
     */
425
    public static function findRootForCategoryGroup($id = null)
426
    {
427
        if (null === $id) {
428
            return null;
429
        }
430
431
        return static::find()
432
            ->where([
433
                'category_group_id' => $id,
434
                'parent_id' => static::CATEGORY_PARENT,
435
                'active' => static::CATEGORY_ACTIVE,
436
            ])
437
            ->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])
438
            ->one();
439
    }
440
441
    /**
442
     * @deprecated
443
     * @param $category_group_id
444
     * @param int $level
445
     * @param int $is_active
446
     * @return Category[]
447
     */
448
    public static function getByLevel($category_group_id, $level = 0, $is_active = 1)
449
    {
450
        $cacheKey = "CategoriesByLevel:$category_group_id:$level";
451
        if (false === $models = Yii::$app->cache->get($cacheKey)) {
452
            $models = Category::find()->where(
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
453
                ['category_group_id' => $category_group_id, 'parent_id' => $level, 'active' => $is_active]
454
            )->orderBy('sort_order')->with('images')->all();
455
456
            if (null !== $models) {
457
458
                $cache_tags = [];
459
                foreach ($models as $model) {
460
                    $cache_tags [] = ActiveRecordHelper::getObjectTag($model, $model->id);
461
                }
462
                $cache_tags [] = ActiveRecordHelper::getObjectTag(static::className(), $level);
463
464
465
                Yii::$app->cache->set(
466
                    $cacheKey,
467
                    $models,
468
                    86400,
469
                    new TagDependency(
470
                        [
471
                            'tags' => $cache_tags
472
                        ]
473
                    )
474
                );
475
            }
476
        }
477
        foreach ($models as $model) {
478
            static::$identity_map[$model->id] = $model;
479
        }
480
        return $models;
481
    }
482
483
    /**
484
     * @param int $parentId
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parentId 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...
485
     * @param int|null $isActive
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $isActive a bit more specific; maybe use integer.
Loading history...
486
     * @return Category[]|array
487
     */
488
    public static function getByParentId($parentId = null, $isActive = 1)
489
    {
490
        if (null === $parentId) {
491
            return [];
492
        }
493
        $parentId = intval($parentId);
494
        $cacheKey = "CategoriesByParentId:$parentId" . ':' . (null === $isActive ? 'null' : $isActive);
495
        if (false !== $models = Yii::$app->cache->get($cacheKey)) {
496
            return $models;
497
        }
498
        $query = static::find()
499
            ->where(['parent_id' => $parentId]);
500
        if (null !== $isActive) {
501
            $query->andWhere(['active' => $isActive]);
502
        }
503
        $models = $query->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])
504
            ->with('images')
505
            ->all();
506
507
        if (empty($models)) {
508
            return [];
509
        }
510
        Yii::$app->cache->set(
511
            $cacheKey,
512
            $models,
513
            0,
514
            new TagDependency([
515
                'tags' => array_reduce($models,
516
                    function ($result, $item)
517
                    {
518
                        /** @var Category $item */
519
                        $result[] = ActiveRecordHelper::getObjectTag(static::className(), $item->id);
520
                        return $result;
521
                    },
522
                    [ActiveRecordHelper::getObjectTag(static::className(), $parentId)]
523
                )
524
            ])
525
        );
526
        return $models;
527
    }
528
529
    public function afterSave($insert, $changedAttributes)
530
    {
531
        parent::afterSave($insert, $changedAttributes);
532
533
        static::$identity_map[$this->id] = $this;
534
535
        if (isset($changedAttributes['category_group_id'])) {
536
            foreach ($this->children as $child) {
537
                $child->category_group_id = $this->category_group_id;
538
                $child->save(true, ['category_group_id']);
539
            }
540
        }
541
542
        if (isset($changedAttributes['parent_id'])) {
543
            if (is_null($this->parent) === false) {
544
                $this->category_group_id = $this->parent->category_group_id;
545
                $this->save(true, ['category_group_id']);
546
            }
547
        }
548
    }
549
550
    /**
551
     * Preparation to delete category.
552
     * Deleting related products and inserted categories.
553
     * @return bool
554
     */
555
    public function beforeDelete()
556
    {
557
        if (!parent::beforeDelete()) {
558
            return false;
559
        }
560
        $product = Yii::$container->get(Product::class);
561
        $productObject = Object::getForClass(get_class($product));
562
        switch ($this->deleteMode) {
563
            case self::DELETE_MODE_ALL:
564
                $products =
565
                    !is_null($productObject)
566
                        ? $product::find()
567
                        ->join(
568
                            'INNER JOIN',
569
                            $productObject->categories_table_name . ' pc',
570
                            'pc.object_model_id = product.id'
571
                        )
572
                        ->where('pc.category_id = :id', [':id' => $this->id])
573
                        ->all()
574
                        : [];
575
                break;
576
            case self::DELETE_MODE_MAIN_CATEGORY:
577
                $products = $product::findAll(['main_category_id' => $this->id]);
578
                break;
579
            default:
580
                $products =
581
                    !is_null($productObject)
582
                        ? $product::find()
583
                        ->join(
584
                            'INNER JOIN',
585
                            $productObject->categories_table_name . ' pc',
586
                            'pc.object_model_id = product.id'
587
                        )
588
                        ->join(
589
                            'INNER JOIN',
590
                            $productObject->categories_table_name . ' pc2',
591
                            'pc2.object_model_id = product.id'
592
                        )
593
                        ->where('pc.category_id = :id', [':id' => $this->id])
594
                        ->groupBy('pc2.object_model_id')
595
                        ->having('COUNT(*) = 1')
596
                        ->all()
597
                        : [];
598
                break;
599
        }
600
        foreach ($products as $product) {
601
            $product->delete();
602
        }
603
        foreach ($this->children as $child) {
604
            $child->deleteMode = $this->deleteMode;
605
            $child->delete();
606
        }
607
        if (!is_null($productObject)) {
608
            Yii::$app->db
609
                ->createCommand()
610
                ->delete($productObject->categories_table_name, ['category_id' => $this->id])
611
                ->execute();
612
        }
613
        return true;
614
    }
615
616
    public function afterDelete()
617
    {
618
        FilterSets::deleteAll(['category_id' => $this->id]);
619
        parent::afterDelete();
620
    }
621
622
623
    /**
624
     * Get children menu items with selected depth
625
     * @param int $parentId
626
     * @param null|integer $depth
627
     * @return array
628
     */
629
    public static function getMenuItems($parentId = 0, $depth = null, $fetchModels = false)
630
    {
631
        if ($depth === 0) {
632
            return [];
633
        }
634
        $cacheKey = 'CategoryMenuItems:' . implode(':', [
635
            $parentId,
636
            null === $depth ? 'null' : intval($depth),
637
            intval($fetchModels)
638
        ]) ;
639
        $items = Yii::$app->cache->get($cacheKey);
640
        if ($items !== false) {
641
            return $items;
642
        }
643
        $items = [];
644
        $categories = static::find()->select(['id', 'name', 'category_group_id'])->where(
645
            ['parent_id' => $parentId, 'active' => 1]
646
        )->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])->with('images')->all();
647
        $cache_tags = [
648
            ActiveRecordHelper::getCommonTag(static::className()),
649
        ];
650
651
        /** @var Category $category */
652
        foreach ($categories as $category) {
653
            $items[] = [
654
                'label' => $category->name,
655
                'url' => Url::toRoute(
656
                    [
657
                        '@category',
658
                        'category_group_id' => $category->category_group_id,
659
                        'last_category_id' => $category->id,
660
                    ]
661
                ),
662
                'id' => $category->id,
663
                'model' => $fetchModels ? $category : null,
664
                'items' => static::getMenuItems($category->id, null === $depth ? null : $depth - 1),
665
            ];
666
            $cache_tags[] = ActiveRecordHelper::getObjectTag(static::className(), $category->id);
667
        }
668
        $cache_tags [] = ActiveRecordHelper::getObjectTag(static::className(), $parentId);
669
        Yii::$app->cache->set(
670
            $cacheKey,
671
            $items,
672
            86400,
673
            new TagDependency(
674
                [
675
                    'tags' => $cache_tags,
676
                ]
677
            )
678
        );
679
        return $items;
680
    }
681
682
    /**
683
     * @return FilterSets[]
684
     */
685
    public function filterSets()
686
    {
687
        if ($this->filterSets === null) {
688
            $this->filterSets = FilterSets::getForCategoryId($this->id);
689
        }
690
        return $this->filterSets;
691
    }
692
693
    /**
694
     * @param int $parentId
695
     * @param int|null $categoryGroupId
696
     * @param string $name
697
     * @param bool $dummyObject
698
     * @return Category|null
699
     */
700
    public static function createEmptyCategory($parentId = 0, $categoryGroupId = null, $name = 'Catalog', $dummyObject = false)
701
    {
702
        $categoryGroupId = null === $categoryGroupId ? CategoryGroup::getFirstModel()->id : intval($categoryGroupId);
703
704
        $model = new static();
705
        $model->loadDefaultValues();
706
        $model->parent_id = $parentId;
707
        $model->category_group_id = $categoryGroupId;
708
        $model->h1 = $model->title = $model->name = $name;
709
        $model->slug = Helper::createSlug($model->name);
710
711
        if (!$dummyObject) {
712
            if (!$model->save()) {
713
                return null;
714
            }
715
            $model->refresh();
716
        }
717
718
        return $model;
719
    }
720
721
    /**
722
     * @param int|Product|null $productModel
723
     * @param bool $asMainCategory
724
     * @return bool
725
     */
726
    public function linkProduct($productModel = null, $asMainCategory = false)
727
    {
728
        if ($productModel instanceof Product) {
729
            return $productModel->linkToCategory($this->id, $asMainCategory);
730
        } elseif (is_int($productModel) || is_string($productModel)) {
731
            $productModel = intval($productModel);
732
            $product = Yii::$container->get(Product::class);
733
            if (null !== $productModel = $product::findById($productModel, null)) {
734
                return $productModel->linkToCategory($this->id, $asMainCategory);
735
            }
736
        }
737
738
        return false;
739
    }
740
741
    /**
742
     * @return string
743
     */
744
    public function __toString()
745
    {
746
        return ($this->className() . ':' . $this->id);
747
    }
748
749
    /**
750
     * @return string
751
     */
752
    public function jsonSerialize()
753
    {
754
        return ($this->className() . ':' . $this->id);
755
    }
756
757
    /**
758
     * Process fields before the actual model is saved(inserted or updated)
759
     * @param array $fields
760
     * @param $multipleValuesDelimiter
761
     * @param array $additionalFields
762
     */
763
    public function processImportBeforeSave(array $fields, $multipleValuesDelimiter, array $additionalFields)
764
    {
765
        $_attributes = $this->attributes();
766
        foreach ($fields as $key => $value) {
767
            if (in_array($key, $_attributes)) {
768
                $this->$key = $value;
769
            }
770
        }
771
772 View Code Duplication
        if (empty($this->slug)) {
773
            $this->slug = Helper::createSlug($this->name);
774
        } elseif (mb_strlen($this->slug) > 80) {
775
            $this->slug = mb_substr($this->slug, 0, 80);
776
        }
777
778
        if (empty($this->name)) {
779
            $this->name = 'unnamed-category';
780
        }
781
    }
782
783
    /**
784
     * Process fields after the actual model is saved(inserted or updated)
785
     * @param array $fields
786
     * @param $multipleValuesDelimiter
787
     * @param array $additionalFields
788
     */
789
    public function processImportAfterSave(array $fields, $multipleValuesDelimiter, array $additionalFields)
790
    {
791
    }
792
}
793