Passed
Push — master ( 21d5f2...f0135a )
by Andreas
06:40
created

Setting   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 334
Duplicated Lines 0 %

Test Coverage

Coverage 91.03%

Importance

Changes 6
Bugs 0 Features 1
Metric Value
wmc 70
eloc 139
c 6
b 0
f 1
dl 0
loc 334
ccs 132
cts 145
cp 0.9103
rs 2.8

18 Methods

Rating   Name   Duplication   Size   Complexity  
B setTypeAttribute() 0 23 9
A isEditable() 0 3 1
B getValueAsStringAttribute() 0 26 10
A __construct() 0 4 1
A getValueAsString() 0 9 3
A getValue() 0 11 3
A hasValue() 0 8 6
A has() 0 3 1
B getValueAttribute() 0 27 11
A getEditableAttribute() 0 7 2
A asJson() 0 7 3
A flushCache() 0 4 1
A validateNewValue() 0 8 2
B setValue() 0 23 11
A getValidationRules() 0 15 2
A allSettings() 0 4 1
A boot() 0 12 1
A remove() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Setting 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.

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

1
<?php
2
3
namespace Ottosmops\Settings;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Support\Facades\Cache;
7
use Illuminate\Support\Facades\Validator;
8
use Illuminate\Validation\ValidationException;
9
10
use Ottosmops\Settings\Exceptions\NoKeyIsFound;
11
12
class Setting extends Model
13
{
14
    protected $keyType = 'string';
15
16
    protected $guarded = [];
17
18
    public $timestamps = false;
19
20
    public $incrementing = false;
21
22
    protected $primaryKey = 'key';
23
24
    protected $table;
25
26
    protected $casts = [
27
        'editable' => 'boolean',
28
        'value' => 'array',
29
        'default' => 'array'
30
    ];
31
32 42
    protected function asJson($value)
33
    {
34 42
        if (is_string($value) && $this->type == 'regex') {
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
35 3
            $value = str_replace('\\', 'ç€π', $value);
36 3
            $value = str_replace('/', '@ƻ', $value);
37
        }
38 42
        return json_encode($value, JSON_UNESCAPED_UNICODE);
39
    }
40
41 66
    public function setTypeAttribute($value)
42
    {
43 66
        switch ($value) {
44 66
            case 'arr':
45 63
            case 'array':
46 6
                $this->attributes['type'] = 'array';
47 6
                break;
48 63
            case 'int':
49 60
            case 'integer':
50 12
                $this->attributes['type'] = 'integer';
51 12
                break;
52 54
            case 'bool':
53 51
            case 'boolean':
54 12
                $this->attributes['type'] = 'boolean';
55 12
                break;
56 42
            case 'regex':
57 3
                $this->attributes['type'] = 'regex';
58 3
                break;
59 39
            case 'string':
60 36
                $this->attributes['type'] = 'string';
61 36
                break;
62
            default:
63 3
                throw new \UnexpectedValueException($value);
64
        }
65 63
    }
66
67 66
    public function __construct(array $attributes = [])
68
    {
69 66
        $this->table = config('settings.table', 'settings');
70 66
        parent::__construct($attributes);
71 66
    }
72
73
    /**
74
     * Get the editable status
75
     *
76
     * @param  string  $value
77
     * @return bool
78
     */
79 63
    public function getEditableAttribute($value)
80
    {
81 63
        if (!isset($value)) {
82 60
            return true;
83
        }
84
85 6
        return (bool) $value;
86
    }
87
88
    /**
89
     * Check if setting is editable
90
     *
91
     * @param  string  $value
92
     * @return bool
93
     */
94 3
    public static function isEditable(string $key) : bool
95
    {
96 3
        return Setting::where('key', $key)->first()->editable;
97
    }
98
99
    /**
100
     * Cast a value to the expected type ($this->type)
101
     * Possible types: array, integer, boolean, string
102
     *
103
     * @param  mixed $value
104
     * @return mixed
105
     */
106 57
    public function getValueAttribute($value)
107
    {
108 57
        if ($value === null) {
109 45
            return null;
110
        }
111
112 39
        switch ($this->type) {
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
113 39
            case 'arr':
114 39
            case 'array':
115 6
                return json_decode($value, true);
116 36
            case 'int':
117 36
            case 'integer':
118 6
                return intval($value);
119 33
            case 'bool':
120 33
            case 'boolean':
121 12
                if ($value === "false") {
122 6
                    return false;
123
                }
124 12
                return boolval($value);
125 21
            case 'regex':
126 3
                $value = str_replace('ç€π', '\\', $value);
127 3
                $value = str_replace('@ƻ', '/', $value);
128 3
                return trim($value, '"');
129 18
            case 'string':
130 18
                $value = json_decode($value, true);
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
131
            default:
132 18
                return trim($value, '"');
133
        }
134
    }
135
136 6
    public function getValueAsStringAttribute()
137
    {
138 6
        if ($this->value === null) {
139
            return '';
140
        }
141
142 6
        $type = $this->type ?: gettype($this->value);
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
143 6
        $value = $this->value;
144 6
        switch ($type) {
145 6
            case 'array':
146 6
            case 'object':
147 3
                return json_encode($value, JSON_UNESCAPED_UNICODE);
148 6
            case 'integer':
149 3
                return (string) $value;
150 6
            case 'boolean':
151 3
                if ($value === false) {
152
                    return "false";
153
                }
154 3
                return "true";
155 3
            case 'regex':
156
                $value = str_replace('ç€π', '\\', $value);
157
                $value = str_replace('@ƻ', '/', $value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
158 3
            case 'string':
159 3
                $value =  trim($value, '"');
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
160
            default:
161 3
                return (string) $value;
162
        }
163
    }
164
165
    /**
166
     * Get a type casted setting value
167
     *
168
     * @param  string $key
169
     * @param  mixed  $default is returned if value is empty (except boolean false)
170
     * @return mixed
171
     */
172 39
    public static function getValue(string $key, $default = null)
173
    {
174 39
        if (!self::has($key)) {
175 9
            throw new NoKeyIsFound();
176
        }
177
178 30
        if (self::hasValue($key)) {
179 30
            return \Ottosmops\Settings\Setting::allSettings()[$key]['value'];
180
        }
181
182 3
        return $default;
183
    }
184
185 6
    public static function getValueAsString(string $key, $default = null)
186
    {
187 6
        if (!self::has($key)) {
188
            throw new NoKeyIsFound();
189
        }
190
191 6
        $setting = static::where('key', $key)->first();
192
193 6
        return $setting->valueAsString ?: $default;
194
    }
195
196
    /**
197
    * Check if setting exists
198
    *
199
    * @param $key
200
    * @return bool
201
    */
202 57
    public static function has(string $key) : bool
203
    {
204 57
        return (boolean) isset(self::allSettings()[$key]);
205
    }
206
207
    /**
208
     * If a setting has a value (also boolean false, integer 0 and an empty string are values)
209
     *
210
     * @param  string  $key
211
     * @return boolean
212
     */
213 33
    public static function hasValue(string $key) : bool
214
    {
215 33
        if (self::has($key) && isset(Setting::allSettings()[$key]['value'])) {
216 33
            $value = Setting::allSettings()[$key]['value'];
217 33
            return !empty($value) || $value === false || $value === 0 || $value === '';
218
        }
219
220 6
        return false;
221
    }
222
223
    /**
224
     * Set a new value
225
     * @param string  $key
226
     * @param mixed  $value    // string, integer, boolean or array
227
     * @param boolean
228
     */
229 33
    public static function setValue(string $key, $value = null, $validate = true)
230
    {
231 33
        if (!self::has($key)) {
232
            throw new NoKeyIsFound();
233
        }
234
235 33
        $setting = self::find($key);
236
237 33
        if ($setting->type === 'boolean' && is_string($value)) {
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
238
            $value = ($value === 'false') ? false : $value;
239
            $value = ($value === 'true')  ? true  : $value;
240
        }
241 33
        if ($setting->type === 'integer' && is_string($value) && ctype_digit($value)) {
242
            $value = (int) $value;
243
        }
244
245 33
        if ($validate && !$setting->validateNewValue($value)) {
246 6
            throw new ValidationException(null);
247
        }
248
249 27
        $setting->value = $value;
0 ignored issues
show
Bug introduced by
The property value does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
250
251 27
        return $setting->save();
252
    }
253
254
255
    /**
256
     * Remove a setting
257
     *
258
     * @param $key
259
     * @return bool
260
     */
261 6
    public static function remove(string $key)
262
    {
263 6
        if (self::has($key)) {
264 3
            return self::find($key)->delete();
265
        }
266
267 3
        throw new NoKeyIsFound();
268
    }
269
270
    /**
271
     * Get all the settings
272
     *
273
     * @return mixed
274
     */
275 57
    public static function allSettings() : array
276
    {
277
        return Cache::rememberForever('settings.all', function () {
278 57
            return $settings = self::all()->keyBy('key')->toArray();
0 ignored issues
show
Unused Code introduced by
The assignment to $settings is dead and can be removed.
Loading history...
279 57
        });
280
    }
281
282
    /**
283
     * Helper function: Validate a value against its type and its rules.
284
     * @param  mixed $value
285
     * @return bool
286
     */
287 33
    public function validateNewValue($value) : bool
288
    {
289 33
        if ($this->type === 'regex') {
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
290 3
            $validator = Validator::make([$this->key => $value], [$this->key => 'string']);
0 ignored issues
show
Bug introduced by
The property key does not seem to exist on Ottosmops\Settings\Setting. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
291 3
            return !$validator->fails();
292
        }
293 30
        $validator = Validator::make([$this->key => $value], self::getValidationRules());
294 30
        return !$validator->fails();
295
    }
296
297
    /**
298
     * Get the validation rules for setting fields
299
     *
300
     * @return array
301
     */
302 33
    public static function getValidationRules() : array
303
    {
304 33
        if ('mysql' === \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
305
            return Cache::rememberForever('settings.rules', function () {
306
                return Setting::select(\DB::raw('concat_ws("|", rules, type) as rules, `key`'))
307
                            ->pluck('rules', 'key')
308
                            ->toArray();
309
            });
310
        }
311
312
313
        return Cache::rememberForever('settings.rules', function () {
314 33
            return Setting::select(\DB::raw("printf('%s|%s', rules, type) as rules, `key`"))
315 33
                            ->pluck('rules', 'key')
316 33
                            ->toArray();
317 33
        });
318
    }
319
320
    /**
321
     * Flush the cache
322
     */
323 63
    public static function flushCache()
324
    {
325 63
        Cache::forget('settings.all');
326 63
        Cache::forget('settings.rules');
327 63
    }
328
329
    /**
330
     * The "booting" method of the model.
331
     *
332
     * @return void
333
     */
334 66
    protected static function boot()
335
    {
336 66
        parent::boot();
337
338
        static::deleted(function () {
339 3
            self::flushCache();
340 66
        });
341
        static::updated(function () {
342 33
            self::flushCache();
343 66
        });
344
        static::created(function () {
345 63
            self::flushCache();
346 66
        });
347 66
    }
348
}
349