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 ( c721cc...3ff35f )
by Ivan
09:59
created

ProductController::buildBreadcrumbsArray()   C

Complexity

Conditions 15
Paths 65

Size

Total Lines 68
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 68
rs 5.7335
c 0
b 0
f 0
cc 15
eloc 42
nc 65
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace app\modules\shop\controllers;
4
5
use app\components\Controller;
6
use app\extensions\DefaultTheme\components\BaseWidget;
7
use app\extensions\DefaultTheme\models\ThemeActiveWidgets;
8
use app\extensions\DefaultTheme\models\ThemeWidgets;
9
use app\extensions\DefaultTheme\widgets\FilterSets\Widget;
10
use app\models\Object;
11
use app\models\PropertyStaticValues;
12
use app\models\Search;
13
use app\modules\core\helpers\ContentBlockHelper;
14
use app\modules\core\helpers\EventTriggeringHelper;
15
use app\modules\core\models\ContentBlock;
16
use app\modules\shop\events\ProductPageShowed;
17
use app\modules\shop\exceptions\EmptyFilterHttpException;
18
use app\modules\shop\models\Category;
19
use app\modules\shop\models\Product;
20
use app\traits\DynamicContentTrait;
21
use devgroup\TagDependencyHelper\ActiveRecordHelper;
22
use Yii;
23
use yii\caching\TagDependency;
24
use yii\data\Pagination;
25
use yii\helpers\ArrayHelper;
26
use yii\helpers\Json;
27
use yii\helpers\Url;
28
use yii\web\ForbiddenHttpException;
29
use yii\web\NotFoundHttpException;
30
use yii\web\Response;
31
use yii\web\ServerErrorHttpException;
32
use app\modules\seo\behaviors\SetCanonicalBehavior;
33
34
class ProductController extends Controller
35
{
36
    use DynamicContentTrait;
37
38
    public function behaviors()
39
    {
40
        return [
41
            [
42
                'class' => SetCanonicalBehavior::className()
43
            ]
44
        ];
45
    }
46
47
    /**
48
     * Products listing by category with filtration support.
49
     *
50
     * @return string
51
     * @throws \Exception
52
     * @throws NotFoundHttpException
53
     * @throws ServerErrorHttpException
54
     */
55
    public function actionList()
0 ignored issues
show
Coding Style introduced by
actionList uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
actionList uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
56
    {
57
58
        $request = Yii::$app->request;
59
60
        if (null === $request->get('category_group_id')) {
61
            throw new NotFoundHttpException;
62
        }
63
        $product = Yii::$container->get(Product::class);
64
        if (null === $object = Object::getForClass(get_class($product))) {
65
            throw new ServerErrorHttpException('Object not found.');
66
        }
67
68
        $category_group_id = intval($request->get('category_group_id', 0));
69
70
        $title_append = $request->get('title_append', '');
71
        if (!empty($title_append)) {
72
            $title_append = is_array($title_append) ? implode(' ', $title_append) : $title_append;
73
            unset($_GET['title_append']);
74
        }
75
76
        $title_prepend = $request->get("title_prepend", "");
77
        if (!empty($title_prepend)) {
78
            $title_prepend = is_array($title_prepend) ? implode(" ", $title_prepend) : $title_prepend;
79
            unset($_GET["title_prepend"]);
80
        }
81
82
        $values_by_property_id = $request->get('properties', []);
83
        if (!is_array($values_by_property_id)) {
84
            $values_by_property_id = [$values_by_property_id];
85
        }
86
87
        if (Yii::$app->request->isPost && isset($_POST['properties'])) {
88
            if (is_array($_POST['properties'])) {
89
                foreach ($_POST['properties'] as $key => $value) {
90
                    if (isset($values_by_property_id[$key])) {
91
                        $values_by_property_id[$key] = array_unique(
92
                            ArrayHelper::merge(
93
                                $values_by_property_id[$key],
94
                                $value
95
                            )
96
                        );
97
                    } else {
98
                        $values_by_property_id[$key] = array_unique($value);
99
                    }
100
                }
101
            }
102
        }
103
104
        $selected_category_ids = $request->get('categories', []);
105
        if (!is_array($selected_category_ids)) {
106
            $selected_category_ids = [$selected_category_ids];
107
        }
108
109
        if (null !== $selected_category_id = $request->get('last_category_id')) {
110
            $selected_category_id = intval($selected_category_id);
111
        }
112
113
        $result = $product::filteredProducts(
114
            $category_group_id,
115
            $values_by_property_id,
116
            $selected_category_id,
117
            false,
118
            null,
119
            true,
120
            false
121
        );
122
        /** @var Pagination $pages */
123
        $pages = $result['pages'];
124
        if (Yii::$app->response->is_prefiltered_page) {
125
            $pages->route = '/' . Yii::$app->request->pathInfo;
126
            $pages->params = [
127
128
            ];
129
        }
130
        $allSorts = $result['allSorts'];
131
        $products = $result['products'];
132
133
        // throw 404 if we are at filtered page without any products
134
        if (!Yii::$app->request->isAjax && !empty($values_by_property_id) && empty($products)) {
135
            throw new EmptyFilterHttpException();
136
        }
137
138
        if (null !== $selected_category = $selected_category_id) {
139
            if ($selected_category_id > 0) {
140
                if (null !== $selected_category = Category::findById($selected_category_id, null)) {
141 View Code Duplication
                    if (!empty($selected_category->meta_description)) {
142
                        $this->view->registerMetaTag(
0 ignored issues
show
Bug introduced by
The method registerMetaTag does only exist in yii\web\View, but not in yii\base\View.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
143
                            [
144
                                'name' => 'description',
145
                                'content' => ContentBlockHelper::compileContentString(
146
                                    $selected_category->meta_description,
147
                                    get_class($product) . ":{$selected_category->id}:meta_description",
148
                                    new TagDependency(
149
                                        [
150
                                            'tags' => [
151
                                                ActiveRecordHelper::getCommonTag(ContentBlock::className()),
152
                                                ActiveRecordHelper::getCommonTag(Category::className())
153
                                            ]
154
                                        ]
155
                                    )
156
                                )
157
                            ],
158
                            'meta_description'
159
                        );
160
                    }
161
162
                    $this->view->title = $selected_category->title;
163
                }
164
            }
165
        }
166
        if (is_null($selected_category) || !$selected_category->active) {
167
            throw new NotFoundHttpException;
168
        }
169
170
        if (!empty($title_append)) {
171
            $this->view->title .= " " . $title_append;
172
        }
173
174
        if (!empty($title_prepend)) {
175
            $this->view->title = "{$title_prepend} {$this->view->title}";
176
        }
177
178
        $this->view->blocks['h1'] = $selected_category->h1;
179
        $this->view->blocks['announce'] = $selected_category->announce;
180
        $this->view->blocks['content'] = $selected_category->content;
181
182
        $this->loadDynamicContent($object->id, 'shop/product/list', $request->get());
183
184
        $params = [
185
            'model' => $selected_category,
186
            'selected_category' => $selected_category,
187
            'selected_category_id' => $selected_category_id,
188
            'selected_category_ids' => $selected_category_ids,
189
            'values_by_property_id' => $values_by_property_id,
190
            'products' => $products,
191
            'object' => $object,
192
            'category_group_id' => $category_group_id,
193
            'pages' => $pages,
194
            'title_append' => $title_append,
195
            'selections' => $request->get(),
196
            'breadcrumbs' => $this->buildBreadcrumbsArray($selected_category, null, $values_by_property_id),
197
            'allSorts' => $allSorts,
198
        ];
199
        $viewFile = $this->computeViewFile($selected_category, 'list');
200
201
        if (Yii::$app->request->isAjax) {
202
            Yii::$app->response->format = Response::FORMAT_JSON;
203
204
            $content = $this->renderAjax(
205
                $viewFile,
206
                $params
207
            );
208
            $filters = '';
209
            $activeWidgets = ThemeActiveWidgets::getActiveWidgets();
210
            foreach ($activeWidgets as $activeWidget) {
211
                if ($activeWidget->widget->widget == Widget::className()) {
212
                    /** @var ThemeWidgets $widgetModel */
213
                    $widgetModel = $activeWidget->widget;
214
                    /** @var BaseWidget $widgetClassName */
215
                    $widgetClassName = $widgetModel->widget;
216
                    $widgetConfiguration = Json::decode($widgetModel->configuration_json, true);
217
                    if (!is_array($widgetConfiguration)) {
218
                        $widgetConfiguration = [];
219
                    }
220
                    $activeWidgetConfiguration = Json::decode($activeWidget->configuration_json, true);
221
                    if (!is_array($activeWidgetConfiguration)) {
222
                        $activeWidgetConfiguration = [];
223
                    }
224
                    $config = ArrayHelper::merge($widgetConfiguration, $activeWidgetConfiguration);
225
                    $config['themeWidgetModel'] = $widgetModel;
226
                    $config['partRow'] = $activeWidget->part;
227
                    $config['activeWidget'] = $activeWidget;
228
                    $filters = $widgetClassName::widget($config);
229
                }
230
            }
231
            return [
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('content' =...lues_by_property_id))); (array) is incompatible with the return type documented by app\modules\shop\control...tController::actionList of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
232
                'content' => $content,
233
                'filters' => $filters,
234
                'title' => $this->view->title,
235
                'url' => Url::to(
236
                    [
237
                        '/shop/product/list',
238
                        'last_category_id' => $selected_category_id,
239
                        'category_group_id' => $category_group_id,
240
                        'properties' => $values_by_property_id
241
                    ]
242
                ),
243
            ];
244
        } else {
245
            return $this->render(
246
                $viewFile,
247
                $params
248
            );
249
        }
250
    }
251
252
    /**
253
     * Product page view
254
     *
255
     * @param null $model_id
256
     * @return string
257
     * @throws NotFoundHttpException
258
     * @throws ServerErrorHttpException
259
     */
260
    public function actionShow($model_id = null)
261
    {
262
        $product = Yii::$container->get(Product::class);
263
        if (null === $object = Object::getForClass(get_class($product))) {
264
            throw new ServerErrorHttpException('Object not found.');
265
        }
266
267
        $productModel = $product::findById($model_id);
268
269
        $request = Yii::$app->request;
270
271
        $values_by_property_id = $request->get('properties', []);
272
        if (!is_array($values_by_property_id)) {
273
            $values_by_property_id = [$values_by_property_id];
274
        }
275
276
        $selected_category_id = $request->get('last_category_id');
277
278
        $selected_category_ids = $request->get('categories', []);
279
        if (!is_array($selected_category_ids)) {
280
            $selected_category_ids = [$selected_category_ids];
281
        }
282
283
        $category_group_id = intval($request->get('category_group_id', 0));
284
285
        // trigger that we are to show product to user!
286
        // wow! such product! very events!
287
        $specialEvent = new ProductPageShowed([
288
            'product_id' => $productModel->id,
289
        ]);
290
        EventTriggeringHelper::triggerSpecialEvent($specialEvent);
291
292
        if (!empty($productModel->meta_description)) {
293
            $this->view->registerMetaTag(
0 ignored issues
show
Bug introduced by
The method registerMetaTag does only exist in yii\web\View, but not in yii\base\View.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
294
                [
295
                    'name' => 'description',
296
                    'content' => ContentBlockHelper::compileContentString(
297
                        $productModel->meta_description,
298
                        get_class($product) . ":{$productModel->id}:meta_description",
299
                        new TagDependency(
300
                            [
301
                                'tags' => [
302
                                    ActiveRecordHelper::getCommonTag(ContentBlock::className()),
303
                                    ActiveRecordHelper::getCommonTag(get_class($product))
304
                                ]
305
                            ]
306
                        )
307
                    )
308
                ],
309
                'meta_description'
310
            );
311
        }
312
313
        $selected_category = ($selected_category_id > 0) ? Category::findById($selected_category_id) : null;
314
315
        $this->view->title = $productModel->title;
316
        $this->view->blocks['h1'] = $productModel->h1;
317
        $this->view->blocks['announce'] = $productModel->announce;
318
        $this->view->blocks['content'] = $productModel->content;
319
        $this->view->blocks['title'] = $productModel->title;
320
321
322
        return $this->render(
323
            $this->computeViewFile($productModel, 'show'),
324
            [
325
                'model' => $productModel,
326
                'category_group_id' => $category_group_id,
327
                'values_by_property_id' => $values_by_property_id,
328
                'selected_category_id' => $selected_category_id,
329
                'selected_category' => $selected_category,
330
                'selected_category_ids' => $selected_category_ids,
331
                'object' => $object,
332
                'breadcrumbs' => $this->buildBreadcrumbsArray($selected_category, $productModel)
0 ignored issues
show
Bug introduced by
It seems like $selected_category defined by $selected_category_id > ...ted_category_id) : null on line 313 can be null; however, app\modules\shop\control...buildBreadcrumbsArray() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
333
            ]
334
        );
335
    }
336
337
    /**
338
     * Search handler
339
     * @return array
340
     * @throws ForbiddenHttpException
341
     */
342
    public function actionSearch()
343
    {
344
        $headers = Yii::$app->response->getHeaders();
345
        $headers->set('X-Robots-Tag', 'none');
346
        $headers->set('X-Frame-Options', 'SAMEORIGIN');
347
        $headers->set('X-Content-Type-Options', 'nosniff');
348
        if (!Yii::$app->request->isAjax) {
349
            throw new ForbiddenHttpException();
350
        }
351
        $model = new Search();
352
        $model->load(Yii::$app->request->get());
353
        $cacheKey = 'ProductSearchIds: ' . $model->q;
354
        $ids = Yii::$app->cache->get($cacheKey);
355
        $product = Yii::$container->get(Product::class);
356
        if ($ids === false) {
357
            $ids = ArrayHelper::merge(
358
                $model->searchProductsByDescription(),
359
                $model->searchProductsByProperty()
360
            );
361
            Yii::$app->cache->set(
362
                $cacheKey,
363
                $ids,
364
                86400,
365
                new TagDependency(
366
                    [
367
                        'tags' => ActiveRecordHelper::getCommonTag(get_class($product)),
368
                    ]
369
                )
370
            );
371
        }
372
373
        /** @var \app\modules\shop\ShopModule $module */
374
        $module = Yii::$app->modules['shop'];
375
376
        $pages = new Pagination(
377
            [
378
                'defaultPageSize' => $module->searchResultsLimit,
379
                'forcePageParam' => false,
380
                'totalCount' => count($ids),
381
            ]
382
        );
383
        $cacheKey .= ' : ' . $pages->offset;
384
        $products = Yii::$app->cache->get($cacheKey);
385 View Code Duplication
        if ($products === false) {
386
            $products = $product::find()->where(
387
                [
388
                    'in',
389
                    '`id`',
390
                    array_slice(
391
                        $ids,
392
                        $pages->offset,
393
                        $pages->limit
394
                    )
395
                ]
396
            )->addOrderBy('sort_order')->with('images')->all();
397
            Yii::$app->cache->set(
398
                $cacheKey,
399
                $products,
400
                86400,
401
                new TagDependency(
402
                    [
403
                        'tags' => ActiveRecordHelper::getCommonTag(get_class($product)),
404
                    ]
405
                )
406
            );
407
        }
408
        Yii::$app->response->format = Response::FORMAT_JSON;
409
        return [
410
            'view' => $this->renderAjax(
411
                'search',
412
                [
413
                    'model' => $model,
414
                    'pages' => $pages,
415
                    'products' => $products,
416
                ]
417
            ),
418
            'totalCount' => count($ids),
419
        ];
420
421
    }
422
423
    /**
424
     * This function build array for widget "Breadcrumbs"
425
     * @param Category $selCat - model of current category
426
     * @param Product|null $product - model of product, if current page is a page of product
427
     * @param array $properties - array of properties and static values
428
     * Return an array for widget or empty array
429
     */
430
    private function buildBreadcrumbsArray($selCat, $product = null, $properties = [])
431
    {
432
        if ($selCat === null) {
433
            return [];
434
        }
435
436
        // init
437
        $breadcrumbs = [];
438
        if ($product !== null && !empty($selCat) && empty($product->breadcrumbs_label) === false) {
439
            $crumbs[$product->breadcrumbs_label] = Url::to(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$crumbs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $crumbs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
440
                [
441
                    '@product',
442
                    'model' => $product,
443
                    'category_group_id' => $selCat->category_group_id,
444
                ]
445
            );
446
        }
447
        // get basic data
448
        $parent = empty($selCat) === false ? $selCat : null;
449
        while ($parent !== null) {
450
            $crumbs[$parent->breadcrumbs_label] = [
0 ignored issues
show
Bug introduced by
The variable $crumbs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
451
                '@category',
452
                'last_category_id' => $parent->id,
453
                'category_group_id' => $parent->category_group_id,
454
            ];
455
            $parent = $parent->parent;
456
        }
457
458
        // build array for widget
459
        $crumbs = array_reverse($crumbs, true);
460
        foreach ($crumbs as $label => $url) {
461
            $breadcrumbs[] = [
462
                'label' => $label,
463
                'url' => $url
464
            ];
465
        }
466
        if (is_null($product) && $this->module->showFiltersInBreadcrumbs && !empty($properties)) {
467
            $route = [
468
                '@category',
469
                'last_category_id' => $selCat->id,
470
                'category_group_id' => $selCat->category_group_id,
471
            ];
472
            $params = [];
473
            foreach ($properties as $propertyId => $propertyStaticValues) {
474
                $localParams = $params;
475
                foreach ((array)$propertyStaticValues as $propertyStaticValue) {
476
                    $psv = PropertyStaticValues::findById($propertyStaticValue);
477
                    if (is_null($psv)) {
478
                        continue;
479
                    }
480
                    $localParams[$propertyId][] = $propertyStaticValue;
481
                    $breadcrumbs[] = [
482
                        'label' => $psv['name'],
483
                        'url' => array_merge($route, ['properties' => $localParams]),
484
                    ];
485
                }
486
                $params[$propertyId] = $propertyStaticValues;
487
            }
488
        }
489
        unset($breadcrumbs[count($breadcrumbs) - 1]['url']); // last item is not a link
490
491
        if (isset(Yii::$app->response->blocks['breadcrumbs_label'])) {
492
            // last item label rewrited through prefiltered page or something similar
493
            $breadcrumbs[count($breadcrumbs) - 1]['label'] = Yii::$app->response->blocks['breadcrumbs_label'];
494
        }
495
496
        return $breadcrumbs;
497
    }
498
}
499