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 ( 34fb17...bef128 )
by Ivan
11:12
created

Widget   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 400
Duplicated Lines 14.5 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 65
lcom 1
cbo 10
dl 58
loc 400
rs 3.2
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
B removeLostDependencies() 0 33 9
A getEavSliderFilter() 0 31 4
A getTableInheritanceFilter() 0 30 3
A getSelectedPropertyIndex() 0 7 2
A mergeUrlProperties() 0 16 5
F widgetRun() 58 227 42

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

1
<?php
2
3
namespace app\extensions\DefaultTheme\widgets\FilterSets;
4
5
use app\extensions\DefaultTheme\components\BaseWidget;
6
use app\models\Property;
7
use app\models\PropertyStaticValues;
8
use app\modules\shop\models\FilterSets;
9
use app\modules\shop\models\Product;
10
use devgroup\TagDependencyHelper\ActiveRecordHelper;
11
use Yii;
12
use yii\caching\TagDependency;
13
use yii\helpers\Json;
14
use yii\helpers\Url;
15
16
/**
17
 * Class Widget
18
 * @package app\extensions\DefaultTheme\widgets\FilterSets
19
 */
20
class Widget extends BaseWidget
21
{
22
    protected $toUnset = [];
23
    public $viewFile = 'filter-sets';
24
    public $hideEmpty = true;
25
    public $usePjax = true;
26
    public $useNewFilter = false;
27
28
    /**
29
     * Remove lost dependencies from url params.
30
     * Also this method builds toUnset array.
31
     * @param FilterSets[] $filterSets
32
     * @param array $urlParams
33
     * @return array
34
     */
35
    protected function removeLostDependencies($filterSets, $urlParams)
36
    {
37
        $depends = [];
38
        $this->toUnset = [];
39
        foreach ($filterSets as $set) {
40
            if (empty($set->property->depends_on_property_id) === false) {
41
                if (isset($this->toUnset[$set->property->depends_on_property_id])) {
42
                    $this->toUnset[$set->property->depends_on_property_id][] = $set->property->id;
43
                } else {
44
                    $this->toUnset[$set->property->depends_on_property_id] = [$set->property->id];
45
                }
46
                if (false === isset($depends[$set->property->id])) {
47
                    $depends[$set->property->id] = [
48
                        $set->property->depends_on_property_id => $set->property->depended_property_values
49
                    ];
50
                } else {
51
                    $depends[$set->property->id][$set->property->depends_on_property_id] =
52
                        $set->property->depended_property_values;
53
                }
54
            }
55
        }
56
        foreach ($depends as $prop_id => $depend) {
57
            $key = key($depend);
58
            if (isset($urlParams['properties'][$prop_id])) {
59
                if (false === isset($urlParams['properties'][$key])) {
60
                    unset($urlParams['properties'][$prop_id]);
61
                } elseif (in_array($depend[$key], $urlParams['properties'][$key], true) === false) {
62
                    unset($urlParams['properties'][$prop_id]);
63
                }
64
            }
65
        }
66
        return $urlParams;
67
    }
68
69
    /**
70
     * @param Property $property
71
     * @param $urlParams
72
     * @return array|null
73
     * @throws \yii\db\Exception
74
     */
75
    public function getEavSliderFilter(Property $property, $urlParams)
76
    {
77
        $result = null;
78
        $key = $property->key;
79
        $eavCategoryData = Yii::$app->db->createCommand(
80
            'SELECT MAX(CAST({{%product_eav}}.value AS DECIMAL)) as max, MIN(CAST({{%product_eav}}.value AS DECIMAL)) as min
81
              FROM {{%product_eav}}
82
              LEFT JOIN {{%product_category}} ON ({{%product_category}}.object_model_id = {{%product_eav}}.object_model_id)
83
              WHERE {{%product_eav}}.key=:key AND {{%product_category}}.category_id=:category_id'
84
        )->bindParam(':key', $key)
85
            ->bindParam(':category_id', $urlParams['last_category_id'])
86
            ->queryOne();
87
88
        if ($eavCategoryData['max'] !== null &&
89
            $eavCategoryData['min'] !== null &&
90
            $eavCategoryData['max'] !== $eavCategoryData['min']
91
        ) {
92
            $result = [
93
                'id' => $property->id,
94
                'name' => $property->name,
95
                'isRange' => 1,
96
                'selections' => [],
97
                'multiple' => 0,
98
                'max' => $eavCategoryData['max'],
99
                'min' => $eavCategoryData['min'],
100
                'property' => $property,
101
                'step' => 1
102
            ];
103
        }
104
        return $result;
105
    }
106
107
    /**
108
     * @param Property $property
109
     * @param $urlParams
110
     * @return array|null
111
     * @throws \yii\db\Exception
112
     */
113
    protected function getTableInheritanceFilter(Property $property, $urlParams)
114
    {
115
        $result = null;
116
        $key = $property->key;
117
        $tableCategoryData = Yii::$app->db->createCommand(
118
            'SELECT MAX({{%product_property}}.[[' . $key . ']]) as max, MIN({{%product_property}}.[[' . $key . ']]) as min
119
              FROM {{%product_property}}
120
              LEFT JOIN {{%product_category}} ON ({{%product_category}}.object_model_id = {{%product_property}}.object_model_id)
121
              WHERE {{%product_category}}.category_id=:category_id'
122
        )
123
            ->bindParam(':category_id', $urlParams['last_category_id'])
124
            ->queryOne();
125
126
        if ($tableCategoryData['max'] !== null &&
127
            $tableCategoryData['min'] !== null
128
        ) {
129
            $result = [
130
                'id' => $property->id,
131
                'name' => $property->name,
132
                'isRange' => 1,
133
                'selections' => [],
134
                'multiple' => 0,
135
                'max' => $tableCategoryData['max'],
136
                'min' => $tableCategoryData['min'],
137
                'property' => $property,
138
                'step' => 1
139
            ];
140
        }
141
        return $result;
142
    }
143
144
145
146
    /**
147
     * Get selected property index
148
     * @param array $properties
149
     * @param integer $propertyId
150
     * @param integer $propertyValueId
151
     * @return string|false
152
     */
153
    protected function getSelectedPropertyIndex($properties, $propertyId, $propertyValueId)
154
    {
155
        if (!isset($properties[$propertyId])) {
156
            return false;
157
        }
158
        return array_search($propertyValueId, $properties[$propertyId], true);
159
    }
160
161
    /**
162
     * Smart url params merging
163
     * @param array $a
164
     * @param array $b
165
     * @return array mixed
166
     */
167
    protected function mergeUrlProperties($a, $b)
168
    {
169
        if (isset($a['properties'], $b['properties'])) {
170
            foreach ($b['properties'] as $propertyId => $staticValues) {
171
                foreach ($staticValues as $staticValue) {
172
                    if (isset($a['properties'][$propertyId])) {
173
                        $a['properties'][$propertyId][] = $staticValue;
174
                    } else {
175
                        $a['properties'][$propertyId] = [$staticValue];
176
                    }
177
                }
178
                $a['properties'][$propertyId] = array_unique($a['properties'][$propertyId]);
179
            }
180
        }
181
        return $a;
182
    }
183
184
    /**
185
     * Actual run function for all widget classes extending BaseWidget
186
     *
187
     * @return mixed
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...
188
     * @throws \yii\base\InvalidConfigException
189
     * @throws \yii\db\Exception
190
     * @throws \yii\di\NotInstantiableException
191
     */
192
    public function widgetRun()
193
    {
194
        $categoryId = Yii::$app->request->get('last_category_id');
195
        if ($categoryId === null) {
196
            return '<!-- no category_id specified for FilterSet widget in request -->';
197
        }
198
        $filterSets = FilterSets::getForCategoryId($categoryId);
199
        if (count($filterSets) === 0) {
200
            return '<!-- no filter sets for current category -->';
201
        }
202
        if ($this->header === '') {
203
            $this->header = Yii::t('app', 'Filters');
204
        }
205
206
        // Begin of a new filter sets implementation
207
        if ($this->useNewFilter) {
208
            $urlParams = [
209
                '@category',
210
                'last_category_id' => $categoryId,
211
                'properties' => Yii::$app->request->get('properties', []),
212
                'category_group_id' => Yii::$app->request->get('category_group_id', 0),
213
            ];
214
            $priceMin = empty(Yii::$app->request->post('price_min')) ? Yii::$app->request->get('price_min') : Yii::$app->request->post('price_min');
215
            $priceMax = empty(Yii::$app->request->post('price_max')) ? Yii::$app->request->get('price_max') : Yii::$app->request->post('price_max');
216
            if (false === empty($priceMin)) {
217
                $urlParams['price_min'] = $priceMin;
218
            }
219
            if (false === empty($priceMax)) {
220
                $urlParams['price_max'] = $priceMax;
221
            }
222
            $urlParams = $this->mergeUrlProperties($urlParams, ['properties' => Yii::$app->request->post('properties', [])]);
223
            $urlParams = $this->removeLostDependencies($filterSets, $urlParams);
224
            $properties = $urlParams['properties'];
225
            $newGet = Yii::$app->request->get();
226
            $newGet['properties'] = $properties;
227
            Yii::$app->request->setQueryParams($newGet);
228
            ksort($properties);
229
            $cacheKey = 'FilterSets:' . $categoryId . ':' . Json::encode($urlParams);
230
            unset($properties);
231
            if (false === $filtersArray = Yii::$app->cache->get($cacheKey)) {
232
                $filtersArray = [];
233
                foreach ($filterSets as $filterSet) {
234
                    /** Если eav и слайдер, то фильтр формируется по особым правилам */
235 View Code Duplication
                    if ($filterSet->is_range_slider && $filterSet->property->is_eav) {
236
                        $filter = $this->getEavSliderFilter($filterSet->property, $urlParams);
237
                        if($filter) {
238
                            $filtersArray[] = $filter;
239
                        }
240
                        continue;
241
                    }
242
243 View Code Duplication
                    if ($filterSet->is_range_slider && $filterSet->property->is_column_type_stored) {
244
                        $filter = $this->getTableInheritanceFilter($filterSet->property, $urlParams);
245
                        if ($filter) {
246
                            $filtersArray[] = $filter;
247
                        }
248
                        continue;
249
                    }
250
251
                    if ($filterSet->property->has_static_values === 0) {
252
                        continue;
253
                    }
254
255
                    if (
256
                        !empty($filterSet->property->depends_on_property_id)
257
                        && !empty($filterSet->property->depended_property_values)
258
                        && $this->getSelectedPropertyIndex(
259
                            $urlParams['properties'],
260
                            $filterSet->property->depends_on_property_id,
261
                            $filterSet->property->depended_property_values
262
                        ) === false
263
                    ) {
264
                        continue;
265
                    }
266
267
                    $selections = PropertyStaticValues::getValuesForFilter(
268
                        $filterSet->property->id,
269
                        $urlParams['last_category_id'],
270
                        $urlParams['properties'],
271
                        $filterSet->multiple,
0 ignored issues
show
Documentation introduced by
$filterSet->multiple is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
272
                        Yii::$app->getModule('shop')->productsFilteringMode
273
                    );
274
                    if (count($selections) === 0) {
275
                        continue;
276
                    }
277
                    $item = [
278
                        'id' => $filterSet->property->id,
279
                        'name' => $filterSet->property->name,
280
                        'sort_order' => $filterSet->property->sort_order,
281
                        'isRange' => $filterSet->is_range_slider,
282
                        'selections' => [],
283
                        'multiple' => $filterSet->multiple,
284
                    ];
285
                    if ($filterSet->is_range_slider) {
286
                        $item['max'] = PHP_INT_MIN;
287
                        $item['min'] = PHP_INT_MAX;
288
                        $item['property'] = $filterSet->property;
289
                    }
290
                    foreach ($selections as $selection) {
291
                        if ($filterSet->is_range_slider) {
292 View Code Duplication
                            if ((int)$selection['value'] > $item['max']) {
293
                                $item['max'] = (int)$selection['value'];
294
                            }
295 View Code Duplication
                            if ((int)$selection['value'] < $item['min']) {
296
                                $item['min'] = (int)$selection['value'];
297
                            }
298
                        } elseif($item['multiple']) {
299
                            $selectedPropertyIndex = $this->getSelectedPropertyIndex(
300
                                $urlParams['properties'],
301
                                $filterSet->property_id,
302
                                $selection['id']
303
                            );
304 View Code Duplication
                            if ($selectedPropertyIndex !== false) {
305
                                if (count($urlParams['properties'][$filterSet->property_id]) > 1) {
306
                                    $routeParams = $urlParams;
307
                                    unset($routeParams['properties'][$filterSet->property_id][$selectedPropertyIndex]);
308
                                } else {
309
                                    $routeParams = $urlParams;
310
                                    unset($routeParams['properties'][$filterSet->property_id]);
311
                                }
312
                                if (isset($this->toUnset[$filterSet->property_id])) {
313
                                    foreach ($this->toUnset[$filterSet->property_id] as $id) {
314
                                        unset($routeParams['properties'][$id]);
315
                                    }
316
                                }
317
                            } else {
318
                                $routeParams = $this->mergeUrlProperties(
319
                                    $urlParams,
320
                                    ['properties' => [$filterSet->property_id => [$selection['id']]]]
321
                                );
322
                            }
323
                            $item['selections'][] = [
324
                                'id' => $selection['id'],
325
                                'checked' => $selectedPropertyIndex !== false,
326
                                'label' => $selection['name'],
327
                                'url' =>  Url::toRoute($routeParams),
328
                                'active' => $selection['active'],
329
                            ];
330
                        } else {
331
                            $selectedPropertyIndex = $this->getSelectedPropertyIndex(
332
                                $urlParams['properties'],
333
                                $filterSet->property_id,
334
                                $selection['id']
335
                            );
336 View Code Duplication
                            if ($selectedPropertyIndex !== false) {
337
                                if (count($urlParams['properties'][$filterSet->property_id]) > 1) {
338
                                    $routeParams = $urlParams;
339
                                    unset($routeParams['properties'][$filterSet->property_id][$selectedPropertyIndex]);
340
                                } else {
341
                                    $routeParams = $urlParams;
342
                                    unset($routeParams['properties'][$filterSet->property_id]);
343
                                }
344
                                if (isset($this->toUnset[$filterSet->property_id])) {
345
                                    foreach ($this->toUnset[$filterSet->property_id] as $id) {
346
                                        unset($routeParams['properties'][$id]);
347
                                    }
348
                                }
349
                            } else {
350
                                $routeParams = $this->mergeUrlProperties(
351
                                    $urlParams,
352
                                    ['properties' => [$filterSet->property_id => [$selection['id']]]]
353
                                );
354
                            }
355
                            $item['selections'][] = [
356
                                'id' => $selection['id'],
357
                                'checked' => $selectedPropertyIndex !== false,
358
                                'label' => $selection['name'],
359
                                'url' => $selection['active'] === true || $selectedPropertyIndex !== false
360
                                    ? Url::toRoute($routeParams)
361
                                    : null,
362
                                'active' => $selection['active'],
363
                            ];
364
                        }
365
                    }
366
                    if ($filterSet->is_range_slider) {
367
                        if ($item['min'] === $item['max']) {
368
                            continue;
369
                        }
370
                        $i = 1;
371
                        $n = $item['max'] - $item['min'];
372
                        while ($n >= 10) {
373
                            $n /= 10;
374
                            $i++;
375
                        }
376
                        $item['step'] = $i > 3 ? 10 ** ($i - 3) : 1;
377
                        unset($i, $n);
378
                    }
379
                    $filtersArray[] = $item;
380
                    unset($item);
381
                }
382
                $product = Yii::$container->get(Product::class);
383
                Yii::$app->cache->set(
384
                    $cacheKey,
385
                    $filtersArray,
386
                    86400,
387
                    new TagDependency(
388
                        [
389
                            'tags' => [
390
                                ActiveRecordHelper::getCommonTag(FilterSets::class),
391
                                ActiveRecordHelper::getCommonTag(get_class($product)),
392
                                ActiveRecordHelper::getCommonTag(Property::class),
393
                            ],
394
                        ]
395
                    )
396
                );
397
            }
398
            return $this->render(
399
                'filter-sets-new',
400
                [
401
                    'filtersArray' => $filtersArray,
402
                    'id' => 'filter-set-' . $this->id,
403
                    'urlParams' => $urlParams,
404
                    'usePjax' => $this->usePjax,
405
                ]
406
            );
407
        }
408
        // End of a new filter sets implementation
409
410
        return $this->render(
411
            $this->viewFile,
412
            [
413
                'filterSets' => $filterSets,
414
                'id' => 'filter-set-' . $this->id,
415
                'hideEmpty' => $this->hideEmpty,
416
            ]
417
        );
418
    }
419
}
420