SystemParamsService   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Importance

Changes 0
Metric Value
wmc 40
lcom 2
cbo 7
dl 0
loc 262
rs 9.2
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getParam() 0 8 2
B prepareParams() 0 21 6
A loadParamsFromCache() 0 10 4
A loadParamsFromDb() 0 15 2
C mergeParams() 0 63 14
A flushCache() 0 6 2
A getArrayKeysRecursivelyInString() 0 16 3
A getParamValueByKey() 0 13 4
A generateArrayForParamValue() 0 19 3

How to fix   Complexity   

Complex Class

Complex classes like SystemParamsService 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 SystemParamsService, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace yiicod\systemparams;
4
5
use RecursiveArrayIterator;
6
use RecursiveIteratorIterator;
7
use Yii;
8
use yii\db\ActiveRecord;
9
use yii\helpers\ArrayHelper;
10
use yii\helpers\Json;
11
12
/**
13
 * Class SystemParamService
14
 * System params service to work with
15
 *
16
 * @author Virchenko Maksim <[email protected]>
17
 *
18
 * @package yiicod\systemparam
19
 */
20
class SystemParamsService
21
{
22
    const ARRAY_KEYS_SEPARATOR = '.';
23
    const PARAM_IS_NOT_SET = 'CONFIG_VALUE_IS_NOT_SET';
24
    const CACHE_KEY = 'YII_SYSTEM_PARAM';
25
26
    /**
27
     * Cache duration
28
     *
29
     * @var int
30
     */
31
    public $cacheDuration = 0;
32
33
    /**
34
     * Params array
35
     *
36
     * @var array
37
     */
38
    private $params = [];
39
40
    /**
41
     * Get system param
42
     *
43
     * @param string $param
44
     *
45
     * @return mixed
46
     */
47
    public function getParam(string $param)
48
    {
49
        if (empty($this->params)) {
50
            $this->prepareParams();
51
        }
52
53
        return ArrayHelper::getValue($this->params, $param);
54
    }
55
56
    /**
57
     * Prepare params
58
     *
59
     * @return bool
60
     */
61
    protected function prepareParams()
62
    {
63
        $paramModel = Yii::$app->get('systemparams')->modelMap['systemParam']['class'];
64
65
        //If table not exists
66
        if (Yii::$app instanceof \yii\console\Application && null === Yii::$app->db->schema->getTableSchema($paramModel::tableName())) {
67
            Yii::error('Table ' . $paramModel::tableName() . ' not exists');
68
69
            return false;
70
        }
71
72
        if (0 === $this->cacheDuration || false === $this->loadParamsFromCache()) {
73
            $this->loadParamsFromDb();
74
            //Set params to cache
75
            if (isset(Yii::$app->cache)) {
76
                Yii::$app->cache->set(self::CACHE_KEY, Json::encode($this->params), $this->cacheDuration);
77
            }
78
        }
79
80
        return true;
81
    }
82
83
    /**
84
     * Load params from db.
85
     *
86
     * @return bool
87
     */
88
    protected function loadParamsFromCache()
89
    {
90
        if ((isset(Yii::$app->cache) && $this->cacheDuration > 0 && false !== Yii::$app->cache->get(self::CACHE_KEY))) {
91
            $this->params = Json::decode(Yii::$app->cache->get(self::CACHE_KEY));
0 ignored issues
show
Documentation Bug introduced by
It seems like \yii\helpers\Json::decod...->get(self::CACHE_KEY)) of type * is incompatible with the declared type array of property $params.

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...
92
93
            return true;
94
        }
95
96
        return false;
97
    }
98
99
    /**
100
     * Load params from db.
101
     *
102
     * @return bolean
103
     */
104
    protected function loadParamsFromDb()
105
    {
106
        $paramModel = Yii::$app->get('systemparams')->modelMap['systemParam']['class'];
107
108
        $records = $paramModel::find()->asArray()->all();
109
110
        foreach ($records as $record) {
111
            $this->params = ArrayHelper::merge($this->params, $this->generateArrayForParamValue(
112
                $record[$paramModel::attributesMap()['fieldParamKey']],
113
                $record[$paramModel::attributesMap()['fieldParamValue']]
114
            ));
115
        }
116
117
        return true;
118
    }
119
120
    /**
121
     * Merge params
122
     *
123
     * @param $params
124
     * @param bool $autoUpdate
125
     *
126
     * @return bool
127
     */
128
    public function mergeParams(array $params, bool $autoUpdate = false)
129
    {
130
        $paramModel = Yii::$app->get('systemparams')->modelMap['systemParam']['class'];
131
132
        $arrayOfKeys = $this->getArrayKeysRecursivelyInString($params);
133
134
        $records = $paramModel::find()->all();
135
136
        foreach ($records as $i => $model) {
137
            $param = $model->{$paramModel::attributesMap()['fieldParamKey']};
138
            if (self::PARAM_IS_NOT_SET !== $this->getParamValueByKey($param, $params)) {
139
                $records[$param] = clone $model;
140
            } else {
141
                $model->delete();
142
            }
143
            unset($records[$i]);
144
        }
145
146
        foreach ($arrayOfKeys as $paramKey) {
147
            /** @var ActiveRecord $model */
148
            $model = new $paramModel();
149
            if (isset($records[$paramKey])) {
150
                $model->setIsNewRecord(false);
151
                $model->setAttributes($records[$paramKey]->getAttributes(), false);
152
            }
153
154
            $model->{$paramModel::attributesMap()['fieldParamKey']} = $paramKey;
155
156
            $value = $this->getParamValueByKey($paramKey, $params);
157
158
            if (false === empty($value)) {
159
                $model->{$paramModel::attributesMap()['fieldParamValue']} = is_bool($value['value']) ? intval($value['value']) : $value['value'];
160
                $model->{$paramModel::attributesMap()['fieldValidator']} = isset($value['validator']) ? $value['validator'] : 'string';
161
                $model->{$paramModel::attributesMap()['fieldDescription']} = isset($value['description']) ? $value['description'] : '';
162
            } else {
163
                $model->{$paramModel::attributesMap()['fieldParamValue']} = is_bool($value) ? intval($value) : $value;
164
                $model->{$paramModel::attributesMap()['fieldValidator']} = 'string';
165
                $model->{$paramModel::attributesMap()['fieldDescription']} = '';
166
            }
167
168
            if ($model->isNewRecord || (count(array_diff(
169
                        [
170
                            $model->{$paramModel::attributesMap()['fieldParamValue']},
171
                            $model->{$paramModel::attributesMap()['fieldValidator']},
172
                            $model->{$paramModel::attributesMap()['fieldDescription']},
173
                        ],
174
                        [
175
                            $records[$paramKey]->{$paramModel::attributesMap()['fieldParamValue']},
176
                            $records[$paramKey]->{$paramModel::attributesMap()['fieldValidator']},
177
                            $records[$paramKey]->{$paramModel::attributesMap()['fieldDescription']},
178
                        ]
179
                    )) && $autoUpdate)
180
            ) {
181
                if (false == $model->save()) {
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...
182
                    Yii::error($paramKey . ' ' . Json::encode($model->getErrors()), 'system.systemparam');
183
                }
184
            }
185
        }
186
187
        self::flushCache();
188
189
        return true;
190
    }
191
192
    /**
193
     * Flush system params cache
194
     */
195
    public static function flushCache()
196
    {
197
        if (isset(Yii::$app->cache)) {
198
            Yii::$app->cache->delete(self::CACHE_KEY);
199
        }
200
    }
201
202
    /**
203
     * Generate array of keys recursively in string.
204
     *
205
     * @param $dataArray
206
     *
207
     * @return array
208
     *
209
     * @author Virchenko Maksim <[email protected]>
210
     */
211
    protected function getArrayKeysRecursivelyInString(array $dataArray): array
212
    {
213
        $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($dataArray));
214
        $keys = [];
215
216
        foreach ($iterator as $key => $value) {
217
            // Build long key name based on parent keys
218
            $key = $iterator->getSubIterator(0)->key();
219
            for ($i = 1; $i < $iterator->getDepth(); ++$i) {
220
                $key = $key . self::ARRAY_KEYS_SEPARATOR . $iterator->getSubIterator($i)->key();
221
            }
222
            $keys[] = $key;
223
        }
224
225
        return array_unique($keys);
226
    }
227
228
    /**
229
     * Return param value or false if param not exist.
230
     *
231
     * @param $pk
232
     * @param $array
233
     *
234
     * @return bool
235
     *
236
     * @author Chaykovskiy Roman
237
     */
238
    protected function getParamValueByKey($pk, $array)
239
    {
240
        $keys = explode(self::ARRAY_KEYS_SEPARATOR, $pk);
241
242
        foreach ($keys as $key) {
243
            $array = isset($array[$key]) ? $array[$key] : self::PARAM_IS_NOT_SET;
244
            if (self::PARAM_IS_NOT_SET == $array) {
245
                break;
246
            }
247
        }
248
249
        return $array;
250
    }
251
252
    /**
253
     * Generate multidimensional array for pk with value.
254
     *
255
     * @param $pk can be string like 'a|b...|n'
256
     * @param $value
257
     *
258
     * @return array
259
     *
260
     * @author Chaykovskiy Roman
261
     */
262
    protected function generateArrayForParamValue($pk, $value)
263
    {
264
        $keys = explode(self::ARRAY_KEYS_SEPARATOR, $pk);
265
        $countOfKeys = count($keys);
266
        $result = [];
267
268
        $current = &$result;
269
        foreach ($keys as $k => $keyValue) {
270
            if ($countOfKeys == ($k + 1)) {
271
                $current[$keyValue] = $value;
272
            } else {
273
                $current[$keyValue] = [];
274
                // descend into the new array
275
                $current = &$current[$keyValue];
276
            }
277
        }
278
279
        return $result;
280
    }
281
}
282