Passed
Push — master ( 410338...af5ddd )
by Andreas
03:41
created

Setting::getValueAsStringAttribute()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 10.6913

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 10
eloc 21
nc 10
nop 0
dl 0
loc 26
ccs 17
cts 21
cp 0.8095
crap 10.6913
rs 7.6666
c 1
b 0
f 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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)) {
35 24
            $value = str_replace('\\', 'ç€π', $value);
36 24
            $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 30
    public static function setValue(string $key, $value = null, $validate = true)
230
    {
231 30
        if (!self::has($key)) {
232
            throw new NoKeyIsFound();
233
        }
234
235 30
        $setting = self::find($key);
236
237 30
        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
242 30
        if ($validate && !$setting->validateNewValue($value)) {
243 6
            throw new ValidationException(null);
244
        }
245
246 24
        $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...
247
248 24
        return $setting->save();
249
    }
250
251
252
    /**
253
     * Remove a setting
254
     *
255
     * @param $key
256
     * @return bool
257
     */
258 6
    public static function remove(string $key)
259
    {
260 6
        if (self::has($key)) {
261 3
            return self::find($key)->delete();
262
        }
263
264 3
        throw new NoKeyIsFound();
265
    }
266
267
    /**
268
     * Get all the settings
269
     *
270
     * @return mixed
271
     */
272 57
    public static function allSettings() : array
273
    {
274
        return Cache::rememberForever('settings.all', function () {
275 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...
276 57
        });
277
    }
278
279
    /**
280
     * Helper function: Validate a value against its type and its rules.
281
     * @param  mixed $value
282
     * @return bool
283
     */
284 30
    public function validateNewValue($value) : bool
285
    {
286 30
        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...
287 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...
288 3
            return !$validator->fails();
289
        }
290 27
        $validator = Validator::make([$this->key => $value], self::getValidationRules());
291 27
        return !$validator->fails();
292
    }
293
294
    /**
295
     * Get the validation rules for setting fields
296
     *
297
     * @return array
298
     */
299 33
    public static function getValidationRules() : array
300
    {
301 33
        if ('mysql' === \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
302
            return Cache::rememberForever('settings.rules', function () {
303
                return Setting::select(\DB::raw('concat_ws("|", rules, type) as rules, `key`'))
304
                            ->pluck('rules', 'key')
305
                            ->toArray();
306
            });
307
        }
308
309
310
        return Cache::rememberForever('settings.rules', function () {
311 33
            return Setting::select(\DB::raw("printf('%s|%s', rules, type) as rules, `key`"))
312 33
                            ->pluck('rules', 'key')
313 33
                            ->toArray();
314 33
        });
315
    }
316
317
    /**
318
     * Flush the cache
319
     */
320 63
    public static function flushCache()
321
    {
322 63
        Cache::forget('settings.all');
323 63
        Cache::forget('settings.rules');
324 63
    }
325
326
    /**
327
     * The "booting" method of the model.
328
     *
329
     * @return void
330
     */
331 66
    protected static function boot()
332
    {
333 66
        parent::boot();
334
335
        static::deleted(function () {
336 3
            self::flushCache();
337 66
        });
338
        static::updated(function () {
339 30
            self::flushCache();
340 66
        });
341
        static::created(function () {
342 63
            self::flushCache();
343 66
        });
344 66
    }
345
}
346