Passed
Push — master ( 31a5ca...982fb5 )
by Andreas
06:02
created

Setting   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 318
Duplicated Lines 0 %

Test Coverage

Coverage 89.31%

Importance

Changes 6
Bugs 0 Features 1
Metric Value
wmc 62
eloc 125
c 6
b 0
f 1
dl 0
loc 318
ccs 117
cts 131
cp 0.8931
rs 3.44

18 Methods

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

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 36
    protected function asJson($value)
33
    {
34 36
        if (is_string($value)) {
35 18
            $value = str_replace('\\', 'ç€π', $value);
36 18
            $value = str_replace('/', '@ƻ', $value);
37
        }
38 36
        return json_encode($value, JSON_UNESCAPED_UNICODE);
39
    }
40
41 60
    public function setTypeAttribute($value)
42
    {
43 60
        switch ($value) {
44 60
            case 'arr':
45 57
            case 'array':
46 6
                $this->attributes['type'] = 'array';
47 6
                break;
48 57
            case 'int':
49 54
            case 'integer':
50 12
                $this->attributes['type'] = 'integer';
51 12
                break;
52 48
            case 'bool':
53 45
            case 'boolean':
54 12
                $this->attributes['type'] = 'boolean';
55 12
                break;
56 36
            case 'string':
57 33
                $this->attributes['type'] = 'string';
58 33
                break;
59
            default:
60 3
                throw new \UnexpectedValueException($value);
61
        }
62 57
    }
63
64 60
    public function __construct(array $attributes = [])
65
    {
66 60
        $this->table = config('settings.table', 'settings');
67 60
        parent::__construct($attributes);
68 60
    }
69
70
    /**
71
     * Get the editable status
72
     *
73
     * @param  string  $value
74
     * @return bool
75
     */
76 57
    public function getEditableAttribute($value)
77
    {
78 57
        if (!isset($value)) {
79 54
            return true;
80
        }
81
82 6
        return (bool) $value;
83
    }
84
85
    /**
86
     * Check if setting is editable
87
     *
88
     * @param  string  $value
89
     * @return bool
90
     */
91 3
    public static function isEditable(string $key) : bool
92
    {
93 3
        return Setting::where('key', $key)->first()->editable;
94
    }
95
96
    /**
97
     * Cast a value to the expected type ($this->type)
98
     * Possible types: array, integer, boolean, string
99
     *
100
     * @param  mixed $value
101
     * @return mixed
102
     */
103 51
    public function getValueAttribute($value)
104
    {
105 51
        if ($value === null) {
106 39
            return null;
107
        }
108
109 33
        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...
110 33
            case 'arr':
111 33
            case 'array':
112 6
                return json_decode($value, true);
113 30
            case 'int':
114 30
            case 'integer':
115 6
                return intval($value);
116 27
            case 'bool':
117 27
            case 'boolean':
118 12
                if ($value === "false") {
119 6
                    return false;
120
                }
121 12
                return boolval($value);
122 15
            case 'string':
123 15
                $value = str_replace('ç€π', '\\', $value);
124 15
                $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...
125
            default:
126 15
                return trim($value, '"');
127
        }
128
    }
129
130 3
    public function getValueAsStringAttribute()
131
    {
132 3
        if ($this->value === null) {
133
            return '';
134
        }
135
136 3
        $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...
137 3
        switch ($type) {
138 3
            case 'array':
139 3
            case 'object':
140 3
                return json_encode($this->value, JSON_UNESCAPED_UNICODE);
141 3
            case 'integer':
142 3
                return (string) $this->value;
143 3
            case 'boolean':
144 3
                if ($this->value === false) {
145
                    return "false";
146
                }
147 3
                return "true";
148
            case 'string':
149
                $this->value = str_replace('ç€π', '\\', $this->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...
150
                $this->value = str_replace('@ƻ', '/', $this->value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
151
            default:
152
                return (string) trim($this->value, '"');
153
        }
154
    }
155
156
    /**
157
     * Get a type casted setting value
158
     *
159
     * @param  string $key
160
     * @param  mixed  $default is returned if value is empty (except boolean false)
161
     * @return mixed
162
     */
163 36
    public static function getValue(string $key, $default = null)
164
    {
165 36
        if (!self::has($key)) {
166 9
            throw new NoKeyIsFound();
167
        }
168
169 27
        if (self::hasValue($key)) {
170 27
            return \Ottosmops\Settings\Setting::allSettings()[$key]['value'];
171
        }
172
173 3
        return $default;
174
    }
175
176 3
    public static function getValueAsString(string $key, $default = null)
177
    {
178 3
        if (!self::has($key)) {
179
            throw new NoKeyIsFound();
180
        }
181
182 3
        $setting = static::where('key', $key)->first();
183
184 3
        return $setting->valueAsString ?: $default;
185
    }
186
187
    /**
188
    * Check if setting exists
189
    *
190
    * @param $key
191
    * @return bool
192
    */
193 51
    public static function has(string $key) : bool
194
    {
195 51
        return (boolean) isset(self::allSettings()[$key]);
196
    }
197
198
    /**
199
     * If a setting has a value (also boolean false, integer 0 and an empty string are values)
200
     *
201
     * @param  string  $key
202
     * @return boolean
203
     */
204 30
    public static function hasValue(string $key) : bool
205
    {
206 30
        if (self::has($key) && isset(Setting::allSettings()[$key]['value'])) {
207 30
            $value = Setting::allSettings()[$key]['value'];
208 30
            return !empty($value) || $value === false || $value === 0 || $value === '';
209
        }
210
211 6
        return false;
212
    }
213
214
    /**
215
     * Set a new value
216
     * @param string  $key
217
     * @param mixed  $value    // string, integer, boolean or array
218
     * @param boolean
219
     */
220 24
    public static function setValue(string $key, $value = null, $validate = true)
221
    {
222 24
        if (!self::has($key)) {
223
            throw new NoKeyIsFound();
224
        }
225
226 24
        $setting = self::find($key);
227
228 24
        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...
229
            $value = ($value === 'false') ? false : $value;
230
            $value = ($value === 'true')  ? true  : $value;
231
        }
232
233 24
        if ($validate && !$setting->validateNewValue($value)) {
234 6
            throw new ValidationException(null);
235
        }
236
237 18
        $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...
238
239 18
        return $setting->save();
240
    }
241
242
243
    /**
244
     * Remove a setting
245
     *
246
     * @param $key
247
     * @return bool
248
     */
249 6
    public static function remove(string $key)
250
    {
251 6
        if (self::has($key)) {
252 3
            return self::find($key)->delete();
253
        }
254
255 3
        throw new NoKeyIsFound();
256
    }
257
258
    /**
259
     * Get all the settings
260
     *
261
     * @return mixed
262
     */
263 51
    public static function allSettings() : array
264
    {
265
        return Cache::rememberForever('settings.all', function () {
266 51
            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...
267 51
        });
268
    }
269
270
    /**
271
     * Helper function: Validate a value against its type and its rules.
272
     * @param  mixed $value
273
     * @return bool
274
     */
275 24
    public function validateNewValue($value) : bool
276
    {
277 24
        $validator = Validator::make([$this->key => $value], self::getValidationRules());
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...
278 24
        return !$validator->fails();
279
    }
280
281
    /**
282
     * Get the validation rules for setting fields
283
     *
284
     * @return array
285
     */
286 30
    public static function getValidationRules() : array
287
    {
288 30
        if ('mysql' === \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
289
            return Cache::rememberForever('settings.rules', function () {
290
                return Setting::select(\DB::raw('concat_ws("|", rules, type) as rules, `key`'))
291
                            ->pluck('rules', 'key')
292
                            ->toArray();
293
            });
294
        }
295
296
297
        return Cache::rememberForever('settings.rules', function () {
298 30
            return Setting::select(\DB::raw("printf('%s|%s', rules, type) as rules, `key`"))
299 30
                            ->pluck('rules', 'key')
300 30
                            ->toArray();
301 30
        });
302
    }
303
304
    /**
305
     * Flush the cache
306
     */
307 57
    public static function flushCache()
308
    {
309 57
        Cache::forget('settings.all');
310 57
        Cache::forget('settings.rules');
311 57
    }
312
313
    /**
314
     * The "booting" method of the model.
315
     *
316
     * @return void
317
     */
318 60
    protected static function boot()
319
    {
320 60
        parent::boot();
321
322
        static::deleted(function () {
323 3
            self::flushCache();
324 60
        });
325
        static::updated(function () {
326 24
            self::flushCache();
327 60
        });
328
        static::created(function () {
329 57
            self::flushCache();
330 60
        });
331 60
    }
332
}
333