Passed
Push — master ( 68a8be...31a5ca )
by Andreas
03:39
created

Setting::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
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
    /**
131
     * Get a type casted setting value
132
     *
133
     * @param  string $key
134
     * @param  mixed  $default is returned if value is empty (except boolean false)
135
     * @return mixed
136
     */
137 36
    public static function getValue(string $key, $default = null)
138
    {
139 36
        if (!self::has($key)) {
140 9
            throw new NoKeyIsFound();
141
        }
142
143 27
        if (self::hasValue($key)) {
144 27
            return \Ottosmops\Settings\Setting::allSettings()[$key]['value'];
145
        }
146
147 3
        return $default;
148
    }
149
150 3
    public static function getValueAsString(string $key, $default = null)
151
    {
152 3
        if (!self::has($key)) {
153
            throw new NoKeyIsFound();
154
        }
155
156 3
        if (self::hasValue($key)) {
157 3
            $value = \Ottosmops\Settings\Setting::allSettings()[$key]['value'];
158
159 3
            $type = gettype($value);
160
161 3
            switch ($type) {
162 3
                case 'array':
163 3
                case 'object':
164 3
                    return json_encode($value, JSON_UNESCAPED_UNICODE);
165 3
                case 'integer':
166 3
                    return (string) $value;
167 3
                case 'boolean':
168 3
                    if ($value === false) {
169
                        return "false";
170
                    }
171 3
                    return "true";
172
                case 'string':
173
                    $value = str_replace('ç€π', '\\', $value);
174
                    $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...
175
                default:
176
                    return (string) trim($value, '"');
177
            }
178
        }
179
180
        return $default;
181
    }
182
183
    /**
184
    * Check if setting exists
185
    *
186
    * @param $key
187
    * @return bool
188
    */
189 51
    public static function has(string $key) : bool
190
    {
191 51
        return (boolean) isset(self::allSettings()[$key]);
192
    }
193
194
    /**
195
     * If a setting has a value (also boolean false, integer 0 and an empty string are values)
196
     *
197
     * @param  string  $key
198
     * @return boolean
199
     */
200 33
    public static function hasValue(string $key) : bool
201
    {
202 33
        if (self::has($key) && isset(Setting::allSettings()[$key]['value'])) {
203 33
            $value = Setting::allSettings()[$key]['value'];
204 33
            return !empty($value) || $value === false || $value === 0 || $value === '';
205
        }
206
207 6
        return false;
208
    }
209
210
    /**
211
     * Set a new value
212
     * @param string  $key
213
     * @param mixed  $value    // string, integer, boolean or array
214
     * @param boolean
215
     */
216 24
    public static function setValue(string $key, $value = null, $validate = true)
217
    {
218 24
        if (!self::has($key)) {
219
            throw new NoKeyIsFound();
220
        }
221
222 24
        $setting = self::find($key);
223
224 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...
225
            $value = ($value === 'false') ? false : $value;
226
            $value = ($value === 'true')  ? true  : $value;
227
        }
228
229 24
        if ($validate && !$setting->validateNewValue($value)) {
230 6
            throw new ValidationException(null);
231
        }
232
233 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...
234
235 18
        return $setting->save();
236
    }
237
238
239
    /**
240
     * Remove a setting
241
     *
242
     * @param $key
243
     * @return bool
244
     */
245 6
    public static function remove(string $key)
246
    {
247 6
        if (self::has($key)) {
248 3
            return self::find($key)->delete();
249
        }
250
251 3
        throw new NoKeyIsFound();
252
    }
253
254
    /**
255
     * Get all the settings
256
     *
257
     * @return mixed
258
     */
259 51
    public static function allSettings() : array
260
    {
261
        return Cache::rememberForever('settings.all', function () {
262 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...
263 51
        });
264
    }
265
266
    /**
267
     * Helper function: Validate a value against its type and its rules.
268
     * @param  mixed $value
269
     * @return bool
270
     */
271 24
    public function validateNewValue($value) : bool
272
    {
273 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...
274 24
        return !$validator->fails();
275
    }
276
277
    /**
278
     * Get the validation rules for setting fields
279
     *
280
     * @return array
281
     */
282 30
    public static function getValidationRules() : array
283
    {
284 30
        if ('mysql' === \DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
285
            return Cache::rememberForever('settings.rules', function () {
286
                return Setting::select(\DB::raw('concat_ws("|", rules, type) as rules, `key`'))
287
                            ->pluck('rules', 'key')
288
                            ->toArray();
289
            });
290
        }
291
292
293
        return Cache::rememberForever('settings.rules', function () {
294 30
            return Setting::select(\DB::raw("printf('%s|%s', rules, type) as rules, `key`"))
295 30
                            ->pluck('rules', 'key')
296 30
                            ->toArray();
297 30
        });
298
    }
299
300
    /**
301
     * Flush the cache
302
     */
303 57
    public static function flushCache()
304
    {
305 57
        Cache::forget('settings.all');
306 57
        Cache::forget('settings.rules');
307 57
    }
308
309
    /**
310
     * The "booting" method of the model.
311
     *
312
     * @return void
313
     */
314 60
    protected static function boot()
315
    {
316 60
        parent::boot();
317
318
        static::deleted(function () {
319 3
            self::flushCache();
320 60
        });
321
        static::updated(function () {
322 24
            self::flushCache();
323 60
        });
324
        static::created(function () {
325 57
            self::flushCache();
326 60
        });
327 60
    }
328
}
329