Passed
Push — master ( 1f0263...68a8be )
by Andreas
04:36
created

Setting   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Test Coverage

Coverage 88.79%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 103
dl 0
loc 287
ccs 95
cts 107
cp 0.8879
rs 8.64
c 5
b 0
f 0
wmc 47

16 Methods

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