Completed
Push — master ( f81666...55a9f3 )
by Alexey
05:42
created

ActiveForm   D

Complexity

Total Complexity 82

Size/Duplication

Total Lines 302
Duplicated Lines 6.62 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 8
Bugs 5 Features 0
Metric Value
wmc 82
c 8
b 5
f 0
lcom 1
cbo 7
dl 20
loc 302
rs 4.8717

8 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 35 8
D getInputs() 0 29 10
D checkRequest() 10 83 26
A draw() 4 9 3
A drawCol() 0 17 2
C getOptionsList() 3 68 21
A drawError() 0 4 1
B checkAccess() 3 20 11

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

1
<?php
2
3
/**
4
 * Active form
5
 *
6
 * @author Alexey Krupskiy <[email protected]>
7
 * @link http://inji.ru/
8
 * @copyright 2015 Alexey Krupskiy
9
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
10
 */
11
12
namespace Ui;
13
14
class ActiveForm extends \Object
15
{
16
    public $model = null;
17
    public $modelName = '';
18
    public $header = "";
19
    public $action = "";
20
    public $form = [];
21
    public $inputs = [];
22
    public $formName = 'noNameForm';
23
    public $requestFormName = '';
24
    public $requestFullFormName = '';
25
    public $parent = null;
26
27
    /**
28
     * 
29
     * @param array|\Model $model
30
     * @param array|string $form
31
     */
32
    public function __construct($model, $form = '')
33
    {
34
        if (is_array($model)) {
35
            $this->form = $model;
36
            if (is_string($form)) {
37
                $this->formName = $form;
38
            }
39
        } else {
40
            $this->model = $model;
41
            $this->modelName = get_class($model);
42
            if (is_array($form)) {
43
                if (empty($form)) {
44
                    throw new \Exception('empty form');
45
                }
46
                $this->form = $form;
47
            } else {
48
                $this->formName = $form;
49
                $this->form = \App::$cur->ui->getModelForm($this->modelName, $form);
50
                if (empty($this->form)) {
51
                    throw new \Exception('empty form');
52
                }
53
                $this->inputs = $this->getInputs();
54
            }
55
        }
56
        $this->requestFormName = "ActiveForm_{$this->formName}";
57
        $modeName = $this->modelName;
58
59
        if (!empty($this->form['name'])) {
60
            $this->header = $this->form['name'];
61
        } elseif (!empty($modeName::$objectName)) {
62
            $this->header = $modeName::$objectName;
63
        } else {
64
            $this->header = $this->modelName;
65
        }
66
    }
67
68
    public function getInputs()
69
    {
70
        $inputs = !empty($this->form['inputs']) ? $this->form['inputs'] : [];
71
        $modelName = $this->modelName;
72
        foreach ($this->form['map'] as $row) {
73
            foreach ($row as $col) {
74
                if (!$col || !empty($inputs[$col])) {
75
                    continue;
76
                }
77
                if (strpos($col, 'form:') === 0) {
78
                    $colPath = explode(':', $col);
79
                    if ($this->model->{$colPath[1]}) {
80
                        $inputs[$col] = new ActiveForm($this->model->{$colPath[1]}, $colPath[2]);
81
                    } else {
82
                        $relOptions = $modelName::getRelation($colPath[1]);
83
                        if (!isset($this->model->_params[$modelName::index()])) {
84
                            $this->model->_params[$modelName::index()] = 0;
85
                        }
86
                        $relOptions['model']::fixPrefix($relOptions['col']);
87
                        $inputs[$col] = new ActiveForm(new $relOptions['model']([ $relOptions['col'] => &$this->model->_params[$modelName::index()]]), $colPath[2]);
88
                    }
89
                    $inputs[$col]->parent = $this;
90
                } elseif (!empty($modelName::$cols[$col])) {
91
                    $inputs[$col] = $modelName::$cols[$col];
92
                }
93
            }
94
        }
95
        return $inputs;
96
    }
97
98
    public function checkRequest($params = [], $ajax = false)
2 ignored issues
show
Coding Style introduced by
checkRequest 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...
Coding Style introduced by
checkRequest 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...
99
    {
100 View Code Duplication
        if (!$this->checkAccess()) {
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...
101
            $this->drawError('you not have access to "' . $this->modelName . '" manager with name: "' . $this->formName . '"');
102
            return [];
103
        }
104
        $successId = 0;
105
        if (!empty($_POST[$this->requestFormName][$this->modelName])) {
106
            $request = $_POST[$this->requestFormName][$this->modelName];
107
            if ($this->model) {
108
                $presets = !empty($this->form['preset']) ? $this->form['preset'] : [];
109 View Code Duplication
                if (!empty($this->form['userGroupPreset'][\Users\User::$cur->group_id])) {
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...
110
                    $presets = array_merge($presets, $this->form['userGroupPreset'][\Users\User::$cur->group_id]);
111
                }
112
                $afterSave = [];
113
                $error = false;
114
                foreach ($this->inputs as $col => $param) {
115
                    if (!empty($presets[$col])) {
116
                        continue;
117
                    }
118
                    if (is_object($param)) {
119
                        $afterSave[] = $param;
120
                        continue;
121
                    }
122 View Code Duplication
                    if (!empty($this->form['userGroupReadonly'][\Users\User::$cur->group_id]) && in_array($col, $this->form['userGroupReadonly'][\Users\User::$cur->group_id])) {
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...
123
                        continue;
124
                    }
125
                    $inputClassName = '\Ui\ActiveForm\Input\\' . ucfirst($param['type']);
126
                    $input = new $inputClassName();
127
                    $input->activeForm = $this;
128
                    $input->activeFormParams = $params;
129
                    $input->modelName = $this->modelName;
130
                    $input->colName = $col;
131
                    $input->colParams = $param;
132
                    try {
133
                        $input->validate($request);
134
                        $input->parseRequest($request);
135
                    } catch (\Exception $exc) {
136
                        \Msg::add($exc->getMessage(), 'danger');
137
                        $error = true;
138
                    }
139
                }
140
                if (!$error) {
141
                    foreach ($presets as $col => $preset) {
142
                        if (!empty($preset['value'])) {
143
                            $this->model->$col = $preset['value'];
144
                        } elseif (!empty($preset['userCol'])) {
145
                            if (strpos($preset['userCol'], ':')) {
146
                                $rel = substr($preset['userCol'], 0, strpos($preset['userCol'], ':'));
147
                                $param = substr($preset['userCol'], strpos($preset['userCol'], ':') + 1);
148
                                $this->model->$col = \Users\User::$cur->$rel->$param;
149
                            } else {
150
                                $this->model->$col = \Users\User::$cur->{$preset['userCol']};
151
                            }
152
                        }
153
                    }
154
                    if (!$this->parent) {
155
                        if (!empty($this->form['successText'])) {
156
                            $text = $this->form['successText'];
157
                        } else {
158
                            $text = $this->model->pk() ? 'Изменения были успешно сохранены' : 'Новый элемент был успешно добавлен';
159
                        }
160
                        \Msg::add($text, 'success');
161
                    }
162
163
                    $this->model->save(!empty($params['dataManagerParams']) ? $params['dataManagerParams'] : []);
164
                    foreach ($afterSave as $form) {
165
                        $form->checkRequest();
166
                    }
167
                    if ($ajax) {
168
                        \Msg::show();
169
                    } elseif (!empty($_GET['redirectUrl'])) {
170
                        \Tools::redirect($_GET['redirectUrl']);
171
                    }
172
                    $successId = $this->model->pk();
173
                }
174
            }
175
            if (!is_array($params) && is_callable($params)) {
176
                $params($request);
177
            }
178
        }
179
        return $successId;
180
    }
181
182
    public function draw($params = [], $ajax = false)
183
    {
184 View Code Duplication
        if (!$this->checkAccess()) {
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...
185
            $this->drawError('you not have access to "' . $this->modelName . '" form with name: "' . $this->formName . '"');
186
            return [];
187
        }
188
        $form = new Form(!empty($this->form['formOptions']) ? $this->form['formOptions'] : []);
189
        \App::$cur->view->widget('Ui\ActiveForm', ['form' => $form, 'activeForm' => $this, 'ajax' => $ajax, 'params' => $params]);
190
    }
191
192
    public function drawCol($colName, $options, $form, $params = [])
193
    {
194
        if (is_object($options)) {
195
            $options->draw();
196
        } else {
197
            $inputClassName = '\Ui\ActiveForm\Input\\' . ucfirst($options['type']);
198
            $input = new $inputClassName();
199
            $input->form = $form;
200
            $input->activeForm = $this;
201
            $input->activeFormParams = $params;
202
            $input->modelName = $this->modelName;
203
            $input->colName = $colName;
204
            $input->colParams = $options;
205
            $input->draw();
206
        }
207
        return true;
208
    }
209
210
    public static function getOptionsList($inputParams, $params = [], $modelName = '', $aditionalInputNamePrefix = 'aditional', $options = [])
211
    {
212
        $values = [];
213
        switch ($inputParams['source']) {
214
            case 'model':
215
                $values = $inputParams['model']::getList(['forSelect' => true]);
216
                break;
217
            case 'array':
218
                $values = $inputParams['sourceArray'];
219
                break;
220
            case 'method':
221
                if (!empty($inputParams['params'])) {
222
                    $values = call_user_func_array([\App::$cur->$inputParams['module'], $inputParams['method']], $inputParams['params']);
223
                } else {
224
                    $values = \App::$cur->$inputParams['module']->$inputParams['method']();
225
                }
226
                break;
227
            case 'relation':
228
                if (!$modelName) {
229
                    return [];
230
                }
231
                $relation = $modelName::getRelation($inputParams['relation']);
232 View Code Duplication
                if (!empty($params['dataManagerParams']['appType'])) {
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...
233
                    $options['appType'] = $params['dataManagerParams']['appType'];
234
                }
235
                $items = [];
236
                if (class_exists($relation['model'])) {
237
                    $filters = $relation['model']::managerFilters();
238
                    if (!empty($filters['getRows']['where'])) {
239
                        $options['where'][] = $filters['getRows']['where'];
240
                    }
241
                    if (!empty($relation['order'])) {
242
                        $options['order'] = $relation['order'];
243
                    }
244
                    if (!empty($inputParams['itemName'])) {
245
                        $options['itemName'] = $inputParams['itemName'];
246
                    }
247
                    $items = $relation['model']::getList($options);
248
                }
249
                if (!empty($params['noEmptyValue'])) {
250
                    $values = [];
251
                } else {
252
                    $values = [0 => 'Не задано'];
253
                }
254
                foreach ($items as $key => $item) {
255
                    if (!empty($inputParams['showCol'])) {
256
                        if (is_array($inputParams['showCol'])) {
257
                            switch ($inputParams['showCol']['type']) {
258
                                case 'staticMethod':
259
                                    $values[$key] = $inputParams['showCol']['class']::{$inputParams['showCol']['method']}($item);
260
                                    break;
261
                            }
262
                        } else {
263
                            $values[$key] = $item->$inputParams['showCol'];
264
                        }
265
                    } else {
266
                        $values[$key] = $item->name();
267
                    }
268
                }
269
                break;
270
        }
271
        foreach ($values as $key => $value) {
272
            if (is_array($value) && !empty($value['input']) && empty($value['input']['noprefix'])) {
273
                $values[$key]['input']['name'] = $aditionalInputNamePrefix . "[{$value['input']['name']}]";
274
            }
275
        }
276
        return $values;
277
    }
278
279
    /**
280
     * Draw error message
281
     * 
282
     * @param string $errorText
283
     */
284
    public function drawError($errorText)
285
    {
286
        echo $errorText;
287
    }
288
289
    /**
290
     * Check access cur user to form with name in param and $model
291
     * 
292
     * @return boolean
293
     */
294
    public function checkAccess()
295
    {
296
        if (empty($this->form)) {
297
            $this->drawError('"' . $this->modelName . '" form with name: "' . $this->formName . '" not found');
298
            return false;
299
        }
300
        if (!empty($this->form['options']['access']['apps']) && !in_array(\App::$cur->name, $this->form['options']['access']['apps'])) {
301
            return false;
302
        }
303
        if ($this->formName == 'manager' && !\Users\User::$cur->isAdmin()) {
304
            return false;
305
        }
306
        if ($this->model && !empty($this->form['options']['access']['self']) && \Users\User::$cur->id == $this->model->user_id) {
307
            return true;
308
        }
309 View Code Duplication
        if (!empty($this->form['options']['access']['groups']) && !in_array(\Users\User::$cur->group_id, $this->form['options']['access']['groups'])) {
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...
310
            return false;
311
        }
312
        return true;
313
    }
314
315
}
316