SmartUpdateAction::getDefaultRules()   C
last analyzed

Complexity

Conditions 10
Paths 1

Size

Total Lines 103

Duplication

Lines 33
Ratio 32.04 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 33
loc 103
ccs 0
cts 83
cp 0
rs 6.1333
c 0
b 0
f 0
cc 10
nc 1
nop 0
crap 110

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
 * HiPanel core package
4
 *
5
 * @link      https://hipanel.com/
6
 * @package   hipanel-core
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2014-2019, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\actions;
12
13
use hipanel\base\Model;
14
use hipanel\base\SearchModelTrait;
15
use hiqdev\hiart\ActiveDataProvider;
16
use Yii;
17
use yii\web\BadRequestHttpException;
18
use yii\web\NotFoundHttpException;
19
20
/**
21
 * Class SmartUpdateAction.
22
 * @property string view
23
 */
24
class SmartUpdateAction extends SwitchAction
25
{
26
    const EVENT_BEFORE_FETCH_LOAD = 'beforeFetchLoad';
27
    const EVENT_BEFORE_FETCH = 'beforeFetch';
28
29
    /**
30
     * @var array|\Closure additional data passed to view
31
     */
32
    public $data = [];
33
34
    /**
35
     * @var string The view that represents current update action
36
     */
37
    public $_view;
38
39
    /**
40
     * @var int|string ID of object to be viewed. Defaults to `$_GET['id']`
41
     */
42
    protected $_id;
43
44
    /**
45
     * @var array additional data passed to model find method
46
     */
47
    public $findOptions = [];
48
49
    /**
50
     * @var Model
51
     */
52
    private $_searchModel;
53
54
    /**
55
     * @var \yii\data\ActiveDataProvider stores ActiveDataProvider after creating by [[getDataProvider]]
56
     * @see getDataProvider()
57
     */
58
    public $dataProvider;
59
60
    /**
61
     * Creates `ActiveDataProvider` with given options list, stores it to [[dataProvider]].
62
     *
63
     * @throws BadRequestHttpException
64
     * @throws \yii\base\InvalidConfigException when failed to generate `WHERE` condition
65
     * @return ActiveDataProvider
66
     */
67
    public function getDataProvider()
68
    {
69
        if ($this->dataProvider === null) {
70
            $this->_id = $this->_id
71
                ?: Yii::$app->request->post('selection')
72
                ?: Yii::$app->request->get('selection')
73
                ?: Yii::$app->request->get('id');
74
75
            $this->dataProvider = $this->getSearchModel()->search([], ['pagination' => false]);
0 ignored issues
show
Bug introduced by
The method search does only exist in hipanel\base\SearchModelTrait, but not in hiqdev\hiart\ActiveRecord.

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...
76
            $this->dataProvider->query->andFilterWhere(
77
                is_array($this->_id) ? ['in', 'id', $this->_id] : ['eq', 'id', $this->_id]
78
            );
79
80
            if (empty($this->dataProvider->query->where)) {
81
                throw new BadRequestHttpException('Where condition is empty!');
82
            }
83
84
            $this->dataProvider->query->andFilterWhere($this->findOptions);
85
        }
86
        $limit = $this->dataProvider->query->limit;
87
        $this->dataProvider->query->andFilterWhere($this->findOptions)->limit($limit ?? -1);
88
89
        return $this->dataProvider;
90
    }
91
92
    /**
93
     * @param $model
94
     */
95
    public function setSearchModel($model)
96
    {
97
        $this->_searchModel = $model;
98
    }
99
100
    /**
101
     * @return Model|SearchModelTrait
102
     */
103
    public function getSearchModel()
104
    {
105
        if (is_null($this->_searchModel)) {
106
            $this->_searchModel = $this->controller->searchModel();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->controller->searchModel() of type object<hiqdev\hiart\ActiveRecord> or object<hipanel\base\SearchModelTrait> is incompatible with the declared type object<hipanel\base\Model> of property $_searchModel.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
107
        }
108
109
        return $this->_searchModel;
110
    }
111
112
    /** {@inheritdoc} */
113
    protected function getDefaultRules()
114
    {
115
        return array_merge(parent::getDefaultRules(), [
116
            'POST xeditable' => [
117
                'class' => XEditableAction::class,
118
            ],
119
            'GET html | POST selection' => [
120
                'class'  => RenderAction::class,
121
                'data'   => $this->data,
122
                'view'   => $this->view,
123 View Code Duplication
                'params' => function ($action) {
0 ignored issues
show
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
                    $models = $this->fetchModels();
125
                    if (empty($models)) {
126
                        throw new NotFoundHttpException('Search result is empty.');
127
                    }
128
                    foreach ($models as $model) {
129
                        $model->scenario = $this->scenario;
0 ignored issues
show
Bug introduced by
The property scenario does not seem to exist. Did you mean _scenario?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
130
                    }
131
132
                    return [
133
                        'models' => $models,
134
                        'model' => reset($models),
135
                    ];
136
                },
137
            ],
138
            'GET ajax' => [
139
                'success' => [
140
                    'class' => RenderAjaxAction::class,
141
                    'data'   => $this->data,
142
                    'view'   => $this->view,
143 View Code Duplication
                    'params' => function ($action) {
0 ignored issues
show
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
                        $models = $this->fetchModels();
145
                        foreach ($models as $model) {
0 ignored issues
show
Bug introduced by
The expression $models of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
146
                            $model->scenario = $this->scenario;
0 ignored issues
show
Bug introduced by
The property scenario does not seem to exist. Did you mean _scenario?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
147
                        }
148
149
                        return [
150
                            'models' => $models,
151
                            'model' => reset($models),
152
                        ];
153
                    },
154
                ],
155
            ],
156
            'POST html' => [
157
                'save'    => true,
158
                'success' => [
159
                    'class' => RedirectAction::class,
160 View Code Duplication
                    'url'   => function ($action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
                        return $action->collection->count() > 1
162
                            ? $action->controller->getSearchUrl()
163
                            : $action->controller->getActionUrl('view', ['id' => $action->collection->first->id]);
164
                    },
165
                ],
166
                'error'   => [
167
                    'class'  => RenderAction::class,
168
                    'view'   => $this->view,
169
                    'data'   => $this->data,
170
                    'params' => function ($action) {
0 ignored issues
show
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
171
                        try {
172
                            $models = $this->fetchModels();
173
174
                            foreach ($models as $model) {
0 ignored issues
show
Bug introduced by
The expression $models of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
175
                                $model->scenario = $this->scenario;
0 ignored issues
show
Bug introduced by
The property scenario does not seem to exist. Did you mean _scenario?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
176
                                foreach ($this->collection->models as $payload) {
177
                                    if ($payload->id === $model->id) {
178
                                        $model->setAttributes(array_filter($payload->getAttributes()));
179
                                    }
180
                                }
181
                            }
182
                        } catch (BadRequestHttpException $e) {
183
                            $models = $this->collection->models;
184
                        }
185
186
                        return [
187
                            'model' => reset($models),
188
                            'models' => $models,
189
                        ];
190
                    },
191
                ],
192
            ],
193
            'POST pjax' => [
194
                'save'    => true,
195
                'success' => [
196
                    'class'  => ProxyAction::class,
197
                    'action' => 'view',
198
                    'params' => function ($action, $model) {
199
                        return ['id' => $model->id];
200
                    },
201
                ],
202
            ],
203
            'POST ajax' => [
204
                'save'    => true,
205
                'success' => [
206
                    'class' => RedirectAction::class,
207 View Code Duplication
                    'url'   => function ($action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
                        return $action->collection->count() > 1
209
                            ? $action->controller->getSearchUrl()
210
                            : $action->controller->getActionUrl('view', ['id' => $action->collection->first->id]);
211
                    },
212
                ],
213
            ],
214
        ]);
215
    }
216
217
    /**
218
     * @return string
219
     */
220
    public function getView()
221
    {
222
        return $this->_view ?: $this->scenario;
0 ignored issues
show
Bug introduced by
The property scenario does not seem to exist. Did you mean _scenario?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
223
    }
224
225
    /**
226
     * @param string $view
227
     */
228
    public function setView($view)
229
    {
230
        $this->_view = $view;
231
    }
232
233
    /**
234
     * Fetches models that will be edited.
235
     *
236
     * @throws BadRequestHttpException
237
     * @return Model[]
238
     */
239
    public function fetchModels()
240
    {
241
        $this->beforeFetchLoad();
242
        $dataProvider = $this->getDataProvider();
243
        $this->beforeFetch();
244
245
        return $dataProvider->getModels();
246
    }
247
248
    public function beforeFetchLoad()
249
    {
250
        $this->trigger(static::EVENT_BEFORE_FETCH_LOAD);
251
    }
252
253
    public function beforeFetch()
254
    {
255
        $this->trigger(static::EVENT_BEFORE_FETCH);
256
    }
257
}
258