Passed
Push — master ( 982fb5...af1fdc )
by Andreas
25:00
created

Setting::getValidationRules()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.351

Importance

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