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.
Test Setup Failed
Push — master ( e91cf6...3a6b3f )
by Alexander
10:43
created

Widget   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 311
Duplicated Lines 1.93 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 54
c 2
b 1
f 0
lcom 1
cbo 9
dl 6
loc 311
rs 7.0642

5 Methods

Rating   Name   Duplication   Size   Complexity  
D removeLostDependencies() 0 35 9
B getEavSliderFilter() 0 31 4
A getSelectedPropertyIndex() 0 7 2
B mergeUrlProperties() 0 15 5
D widgetRun() 6 184 34

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\Url;
14
15
class Widget extends BaseWidget
16
{
17
    protected $toUnset = [];
18
    public $viewFile = 'filter-sets';
19
    public $hideEmpty = true;
20
    public $usePjax = true;
21
    public $useNewFilter = false;
22
23
    /**
24
     * Remove lost dependencies from url params.
25
     * Also this method builds toUnset array.
26
     * @param FilterSets $filterSets
27
     * @param array $urlParams
28
     * @return array
29
     */
30
    protected function removeLostDependencies($filterSets, $urlParams)
31
    {
32
        $depends = [];
33
        $this->toUnset = [];
34
        foreach ($filterSets as $set) {
35
            if (false == empty($set->property->depends_on_property_id)) {
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...
36
                if (isset($this->toUnset[$set->property->depends_on_property_id])) {
37
                    $this->toUnset[$set->property->depends_on_property_id][] = $set->property->id;
38
                } else {
39
                    $this->toUnset[$set->property->depends_on_property_id] = [$set->property->id];
40
                }
41
                if (false === isset($depends[$set->property->id])) {
42
                    $depends[$set->property->id] = [
43
                        $set->property->depends_on_property_id => $set->property->depended_property_values
44
                    ];
45
                } else {
46
                    $depends[$set->property->id][$set->property->depends_on_property_id] =
47
                        $set->property->depended_property_values;
48
                }
49
            }
50
        }
51
        foreach ($depends as $prop_id => $depend) {
52
            $key = key($depend);
53
            if (isset($urlParams['properties'][$prop_id])) {
54
                if (false === isset($urlParams['properties'][$key])) {
55
                    unset($urlParams['properties'][$prop_id]);
56
                } else {
57
                    if (false === in_array($depend[$key], $urlParams['properties'][$key])) {
58
                        unset($urlParams['properties'][$prop_id]);
59
                    }
60
                }
61
            }
62
        }
63
        return $urlParams;
64
    }
65
66
    public function getEavSliderFilter(Property $property, $urlParams)
67
    {
68
        $result = null;
69
        $key = $property->key;
70
        $eavCategoryData = Yii::$app->db->createCommand(
71
            'SELECT MAX(CAST({{%product_eav}}.value AS DECIMAL)) as max, MIN(CAST({{%product_eav}}.value AS DECIMAL)) as min
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
72
              FROM {{%product_eav}}
73
              LEFT JOIN {{%product_category}} ON ({{%product_category}}.object_model_id = {{%product_eav}}.object_model_id)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
74
              WHERE {{%product_eav}}.key=:key AND {{%product_category}}.category_id=:category_id'
75
        )->bindParam(':key', $key)
76
            ->bindParam(':category_id', $urlParams['last_category_id'])
77
            ->queryOne();
78
79
        if ($eavCategoryData['max'] !== null &&
80
            $eavCategoryData['min'] !== null &&
81
            $eavCategoryData['max'] !== $eavCategoryData['min']
82
        ) {
83
            $result = [
84
                'id' => $property->id,
85
                'name' => $property->name,
86
                'isRange' => 1,
87
                'selections' => [],
88
                'multiple' => 0,
89
                'max' => $eavCategoryData['max'],
90
                'min' => $eavCategoryData['min'],
91
                'property' => $property,
92
                'step' => 1
93
            ];
94
        }
95
        return $result;
96
    }
97
98
99
    /**
100
     * Get selected property index
101
     * @param array $properties
102
     * @param integer $propertyId
103
     * @param integer $propertyValueId
104
     * @return string|false
105
     */
106
    protected function getSelectedPropertyIndex($properties, $propertyId, $propertyValueId)
107
    {
108
        if (!isset($properties[$propertyId])) {
109
            return false;
110
        }
111
        return array_search($propertyValueId, $properties[$propertyId]);
112
    }
113
114
    /**
115
     * Smart url params merging
116
     * @param array $a
117
     * @param array $b
118
     * @return array mixed
119
     */
120
    protected function mergeUrlProperties($a, $b)
121
    {
122
        if (isset($a['properties'], $b['properties'])) {
123
            foreach ($b['properties'] as $propertyId => $staticValues) {
124
                foreach ($staticValues as $staticValue) {
125
                    if (isset($a['properties'][$propertyId])) {
126
                        $a['properties'][$propertyId][] = $staticValue;
127
                    } else {
128
                        $a['properties'][$propertyId] = [$staticValue];
129
                    }
130
                }
131
            }
132
        }
133
        return $a;
134
    }
135
136
    /**
137
     * Actual run function for all widget classes extending BaseWidget
138
     *
139
     * @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...
140
     */
141
    public function widgetRun()
142
    {
143
        $categoryId = Yii::$app->request->get('last_category_id');
144
        if ($categoryId === null) {
145
            return '<!-- no category_id specified for FilterSet widget in request -->';
146
        }
147
        $filterSets = FilterSets::getForCategoryId($categoryId);
148
        if (count($filterSets) === 0) {
149
            return '<!-- no filter sets for current category -->';
150
        }
151
        if ($this->header === '') {
152
            $this->header = Yii::t('app', 'Filters');
153
        }
154
155
        // Begin of a new filter sets implementation
156
        if ($this->useNewFilter) {
157
            $urlParams = [
158
                '@category',
159
                'last_category_id' => $categoryId,
160
                'properties' => Yii::$app->request->get('properties', []),
161
                'category_group_id' => Yii::$app->request->get('category_group_id', 0),
162
            ];
163
            $priceMin = empty(Yii::$app->request->post('price_min')) ? Yii::$app->request->get('price_min') : Yii::$app->request->post('price_min');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
164
            $priceMax = empty(Yii::$app->request->post('price_max')) ? Yii::$app->request->get('price_max') : Yii::$app->request->post('price_max');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
165
            if (false === empty($priceMin)) {
166
                $urlParams['price_min'] = $priceMin;
167
            }
168
            if (false === empty($priceMax)) {
169
                $urlParams['price_max'] = $priceMax;
170
            }
171
            $urlParams = $this->mergeUrlProperties($urlParams, ['properties' => Yii::$app->request->post('properties', [])]);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
172
            $urlParams = $this->removeLostDependencies($filterSets, $urlParams);
0 ignored issues
show
Documentation introduced by
$filterSets is of type array<integer,object<app...hop\models\FilterSets>>, but the function expects a object<app\modules\shop\models\FilterSets>.

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...
173
            $properties = $urlParams['properties'];
174
            $newGet = Yii::$app->request->get();
175
            $newGet['properties'] = $properties;
176
            Yii::$app->request->setQueryParams($newGet);
177
            ksort($properties);
178
            $cacheKey = 'FilterSets:' . $categoryId . ':' . json_encode($urlParams);
179
            unset($properties);
180
            if (false === $filtersArray = Yii::$app->cache->get($cacheKey)) {
181
                $filtersArray = [];
182
                foreach ($filterSets as $filterSet) {
183
                    /** Если eav и слайдер, то фильтр формируется по особым правилам */
184
                    if ($filterSet->is_range_slider && $filterSet->property->is_eav) {
185
                        $filter = $this->getEavSliderFilter($filterSet->property, $urlParams);
186
                        if($filter) {
187
                            $filtersArray[] = $filter;
188
                        }
189
                        continue;
190
                    } elseif ($filterSet->property->has_static_values === 0) {
191
                        continue;
192
                    }
193
                    if (!empty($filterSet->property->depends_on_property_id)
194
                        && !empty($filterSet->property->depended_property_values)
195
                    ) {
196
                        if (
197
                            $this->getSelectedPropertyIndex(
198
                                $urlParams['properties'],
199
                                $filterSet->property->depends_on_property_id,
200
                                $filterSet->property->depended_property_values
201
                            ) === false
202
                        ) {
203
                            continue;
204
                        }
205
                    }
206
                    $selections = PropertyStaticValues::getValuesForFilter(
207
                        $filterSet->property->id,
208
                        $urlParams['last_category_id'],
209
                        $urlParams['properties'],
210
                        $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...
211
                        Yii::$app->getModule('shop')->filterOnlyByParentProduct
212
                    );
213
                    if (count($selections) === 0) {
214
                        continue;
215
                    }
216
                    $item = [
217
                        'id' => $filterSet->property->id,
218
                        'name' => $filterSet->property->name,
219
                        'sort_order' => $filterSet->property->sort_order,
220
                        'isRange' => $filterSet->is_range_slider,
221
                        'selections' => [],
222
                        'multiple' => $filterSet->multiple,
223
                    ];
224
                    if ($filterSet->is_range_slider) {
225
                        $item['max'] = 0;
226
                        $item['min'] = PHP_INT_MAX;
227
                        $item['property'] = $filterSet->property;
228
                    }
229
                    foreach ($selections as $selection) {
230
                        if ($filterSet->is_range_slider) {
231 View Code Duplication
                            if ((int)$selection['value'] > $item['max']) {
232
                                $item['max'] = (int)$selection['value'];
233
                            }
234 View Code Duplication
                            if ((int)$selection['value'] < $item['min']) {
235
                                $item['min'] = (int)$selection['value'];
236
                            }
237
                        } else {
238
                            $selectedPropertyIndex = $this->getSelectedPropertyIndex(
239
                                $urlParams['properties'],
240
                                $filterSet->property_id,
241
                                $selection['id']
242
                            );
243
                            if ($selectedPropertyIndex !== false) {
244
                                if (count($urlParams['properties'][$filterSet->property_id]) > 1) {
245
                                    $routeParams = $urlParams;
246
                                    unset($routeParams['properties'][$filterSet->property_id][$selectedPropertyIndex]);
247
                                } else {
248
                                    $routeParams = $urlParams;
249
                                    unset($routeParams['properties'][$filterSet->property_id]);
250
                                }
251
                                if (isset($this->toUnset[$filterSet->property_id])) {
252
                                    foreach ($this->toUnset[$filterSet->property_id] as $id) {
253
                                        unset($routeParams['properties'][$id]);
254
                                    }
255
                                }
256
                            } else {
257
                                $routeParams = $this->mergeUrlProperties(
258
                                    $urlParams,
259
                                    ['properties' => [$filterSet->property_id => [$selection['id']]]]
260
                                );
261
                            }
262
                            $item['selections'][] = [
263
                                'id' => $selection['id'],
264
                                'checked' => $selectedPropertyIndex !== false,
265
                                'label' => $selection['name'],
266
                                'url' => $selection['active'] === true || $selectedPropertyIndex !== false
267
                                    ? Url::toRoute($routeParams)
268
                                    : null,
269
                                'active' => $selection['active'],
270
                            ];
271
                        }
272
                    }
273
                    if ($filterSet->is_range_slider) {
274
                        if ($item['min'] === $item['max']) {
275
                            continue;
276
                        }
277
                        $i = 1;
278
                        $n = $item['max'] - $item['min'];
279
                        while ($n >= 10) {
280
                            $n = $n / 10;
281
                            $i++;
282
                        }
283
                        $item['step'] = $i > 3 ? (int)pow(10, $i - 3) : 1;
284
                        unset($i, $n);
285
                    }
286
                    $filtersArray[] = $item;
287
                    unset($item);
288
                }
289
                Yii::$app->cache->set(
290
                    $cacheKey,
291
                    $filtersArray,
292
                    86400,
293
                    new TagDependency(
294
                        [
295
                            'tags' => [
296
                                ActiveRecordHelper::getCommonTag(FilterSets::className()),
297
                                ActiveRecordHelper::getCommonTag(Product::className()),
298
                                ActiveRecordHelper::getCommonTag(Property::className()),
299
                            ],
300
                        ]
301
                    )
302
                );
303
            }
304
            return $this->render(
305
                $this->viewFile,
306
                [
307
                    'filtersArray' => $filtersArray,
308
                    'id' => 'filter-set-' . $this->id,
309
                    'urlParams' => $urlParams,
310
                    'usePjax' => $this->usePjax,
311
                ]
312
            );
313
        }
314
        // End of a new filter sets implementation
315
316
        return $this->render(
317
            $this->viewFile,
318
            [
319
                'filterSets' => $filterSets,
320
                'id' => 'filter-set-' . $this->id,
321
                'hideEmpty' => $this->hideEmpty,
322
            ]
323
        );
324
    }
325
}
326