Settings::allAllowed()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace LaravelPropertyBag\Settings;
4
5
use Illuminate\Database\Eloquent\Model;
6
use LaravelPropertyBag\Settings\Rules\RuleValidator;
7
use LaravelPropertyBag\Exceptions\InvalidSettingsValue;
8
9
class Settings
10
{
11
    /**
12
     * Settings for resource.
13
     *
14
     * @var \LaravelPropertyBag\Settings\ResourceConfig
15
     */
16
    protected $settingsConfig;
17
18
    /**
19
     * Resource that has settings.
20
     *
21
     * @var Model
22
     */
23
    protected $resource;
24
25
    /**
26
     * Registered keys, values, and defaults.
27
     * 'key' => ['allowed' => $value, 'default' => $value].
28
     *
29
     * @var \Illuminate\Support\Collection
30
     */
31
    protected $registered;
32
33
    /**
34
     * Settings saved in database. Does not include defaults.
35
     *
36
     * @var \Illuminate\Support\Collection
37
     */
38
    protected $settings;
39
40
    /**
41
     * Validator for allowed rules.
42
     *
43
     * @var \LaravelPropertyBag\Settings\Rules\RuleValidator
44
     */
45
    protected $ruleValidator;
46
47
    /**
48
     * Construct.
49
     *
50
     * @param ResourceConfig $settingsConfig
51
     * @param Model          $resource
52
     */
53
    public function __construct(ResourceConfig $settingsConfig, Model $resource)
54
    {
55
        $this->settingsConfig = $settingsConfig;
56
        $this->resource = $resource;
57
58
        $this->ruleValidator = new RuleValidator();
59
        $this->registered = $settingsConfig->registeredSettings();
60
61
        $this->sync();
62
    }
63
64
    /**
65
     * Get the property bag relationshp off the resource.
66
     *
67
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
68
     */
69
    protected function propertyBag()
70
    {
71
        return $this->resource->propertyBag();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->resource->propertyBag() also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Relations\MorphMany.
Loading history...
72
    }
73
74
    /**
75
     * Get resource config.
76
     *
77
     * @return \LaravelPropertyBag\Settings\ResourceConfig
78
     */
79
    public function getResourceConfig()
80
    {
81
        return $this->settingsConfig;
82
    }
83
84
    /**
85
     * Get registered settings.
86
     *
87
     * @return \Illuminate\Support\Collection
88
     */
89
    public function getRegistered()
90
    {
91
        return $this->registered;
92
    }
93
94
    /**
95
     * Return true if key exists in registered settings collection.
96
     *
97
     * @param string $key
98
     *
99
     * @return bool
100
     */
101
    public function isRegistered($key)
102
    {
103
        return $this->getRegistered()->has($key);
104
    }
105
106
    /**
107
     * Return true if key and value are registered values.
108
     *
109
     * @param string $key
110
     * @param mixed  $value
111
     *
112
     * @return bool
113
     */
114
    public function isValid($key, $value)
115
    {
116
        $settings = collect(
117
            $this->getRegistered()->get($key, ['allowed' => []])
118
        );
119
120
        $allowed = $settings->get('allowed');
121
122
        if (!is_array($allowed) &&
123
            $rule = $this->ruleValidator->isRule($allowed)) {
124
            return $this->ruleValidator->validate($rule, $value);
0 ignored issues
show
Bug introduced by
It seems like $rule can also be of type true; however, parameter $rule of LaravelPropertyBag\Setti...leValidator::validate() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
            return $this->ruleValidator->validate(/** @scrutinizer ignore-type */ $rule, $value);
Loading history...
125
        }
126
127
        return in_array($value, $allowed, true);
128
    }
129
130
    /**
131
     * Return true if value is default value for key.
132
     *
133
     * @param string $key
134
     * @param mixed  $value
135
     *
136
     * @return bool
137
     */
138
    public function isDefault($key, $value)
139
    {
140
        return $this->getDefault($key) === $value;
141
    }
142
143
    /**
144
     * Get the default value from registered.
145
     *
146
     * @param string $key
147
     *
148
     * @return mixed
149
     */
150
    public function getDefault($key)
151
    {
152
        if ($this->isRegistered($key)) {
153
            return $this->getRegistered()[$key]['default'];
154
        }
155
    }
156
157
    /**
158
     * Return all settings used by resource, including defaults.
159
     *
160
     * @return \Illuminate\Support\Collection
161
     */
162
    public function all()
163
    {
164
        $saved = $this->allSaved();
165
166
        return $this->allDefaults()->map(function ($value, $key) use ($saved) {
167
            if ($saved->has($key)) {
168
                return $saved->get($key);
169
            }
170
171
            return $value;
172
        });
173
    }
174
175
    /**
176
     * Get all defaults for settings.
177
     *
178
     * @return \Illuminate\Support\Collection
179
     */
180
    public function allDefaults()
181
    {
182
        return $this->getRegistered()->map(function ($value) {
183
            return $value['default'];
184
        });
185
    }
186
187
    /**
188
     * Get the allowed settings for key.
189
     *
190
     * @param string $key
191
     *
192
     * @return \Illuminate\Support\Collection
193
     */
194
    public function getAllowed($key)
195
    {
196
        if ($this->isRegistered($key)) {
197
            return collect($this->getRegistered()[$key]['allowed']);
198
        }
199
    }
200
201
    /**
202
     * Get all allowed values for settings.
203
     *
204
     * @return \Illuminate\Support\Collection
205
     */
206
    public function allAllowed()
207
    {
208
        return $this->getRegistered()->map(function ($value) {
209
            return $value['allowed'];
210
        });
211
    }
212
213
    /**
214
     * Get all saved settings. Default values are not included in this output.
215
     *
216
     * @return \Illuminate\Support\Collection
217
     */
218
    public function allSaved()
219
    {
220
        return collect($this->settings);
221
    }
222
223
    /**
224
     * Update or add multiple values to the settings table.
225
     *
226
     * @param array $attributes
227
     *
228
     * @return static
229
     */
230
    public function set(array $attributes)
231
    {
232
        collect($attributes)->each(function ($value, $key) {
233
            $this->setKeyValue($key, $value);
234
        });
235
236
        // If we were working with eagerly-loaded relation,
237
        // we need to reload its data to be sure that we
238
        // are working only with the actual settings.
239
240
        if ($this->resource->relationLoaded('propertyBag')) {
241
            $this->resource->load('propertyBag');
242
        }
243
244
        return $this->sync();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->sync() targeting LaravelPropertyBag\Settings\Settings::sync() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
245
    }
246
247
    /**
248
     * Return true if key is set to value.
249
     *
250
     * @param string $key
251
     * @param string $value
252
     *
253
     * @return bool
254
     */
255
    public function keyIs($key, $value)
256
    {
257
        return $this->get($key) === $value;
258
    }
259
260
    /**
261
     * Reset key to default value. Return default value.
262
     *
263
     * @param string $key
264
     *
265
     * @return mixed
266
     */
267
    public function reset($key)
268
    {
269
        $default = $this->getDefault($key);
270
271
        $this->set([$key => $default]);
272
273
        return $default;
274
    }
275
276
    /**
277
     * Set a value to a key in local and database settings.
278
     *
279
     * @param string $key
280
     * @param mixed  $value
281
     *
282
     * @return mixed
283
     */
284
    protected function setKeyValue($key, $value)
285
    {
286
        $this->validateKeyValue($key, $value);
287
288
        if ($this->isDefault($key, $value) && $this->isSaved($key)) {
289
            return $this->deleteRecord($key);
290
        } elseif ($this->isDefault($key, $value)) {
291
            return;
292
        } elseif ($this->isSaved($key)) {
293
            return $this->updateRecord($key, $value);
294
        }
295
296
        return $this->createRecord($key, $value);
297
    }
298
299
    /**
300
     * Throw exception if key/value invalid.
301
     *
302
     * @param string $key
303
     * @param mixed  $value
304
     *
305
     * @throws InvalidSettingsValue
306
     */
307
    protected function validateKeyValue($key, $value)
308
    {
309
        if (!$this->isValid($key, $value)) {
310
            throw InvalidSettingsValue::settingNotAllowed($key);
311
        }
312
    }
313
314
    /**
315
     * Return true if key is already saved in database.
316
     *
317
     * @param string $key
318
     *
319
     * @return bool
320
     */
321
    public function isSaved($key)
322
    {
323
        return $this->allSaved()->has($key);
324
    }
325
326
    /**
327
     * Create a new PropertyBag record.
328
     *
329
     * @param string $key
330
     * @param mixed  $value
331
     *
332
     * @return \LaravelPropertyBag\Settings\PropertyBag
333
     */
334
    protected function createRecord($key, $value)
335
    {
336
        return $this->propertyBag()->save(
337
            new PropertyBag([
338
                'key'   => $key,
339
                'value' => $this->valueToJson($value),
340
            ])
341
        );
342
    }
343
344
    /**
345
     * Update a PropertyBag record.
346
     *
347
     * @param string $key
348
     * @param mixed  $value
349
     *
350
     * @return \LaravelPropertyBag\Settings\PropertyBag
351
     */
352
    protected function updateRecord($key, $value)
353
    {
354
        $record = $this->getByKey($key);
355
356
        $record->value = $this->valueToJson($value);
357
358
        $record->save();
359
360
        return $record;
361
    }
362
363
    /**
364
     * Json encode value.
365
     *
366
     * @param mixed $value
367
     *
368
     * @return string
369
     */
370
    protected function valueToJson($value)
371
    {
372
        return json_encode([$value]);
373
    }
374
375
    /**
376
     * Delete a PropertyBag record.
377
     *
378
     * @param string $key
379
     *
380
     * @return bool
381
     */
382
    protected function deleteRecord($key)
383
    {
384
        $this->getByKey($key)->delete();
385
    }
386
387
    /**
388
     * Get a property bag record by key.
389
     *
390
     * @param string $key
391
     *
392
     * @return \LaravelPropertyBag\Settings\PropertyBag
393
     */
394
    protected function getByKey($key)
395
    {
396
        return $this->propertyBag()
397
            ->where('resource_id', $this->resource->getKey())
398
            ->where('key', $key)
399
            ->first();
400
    }
401
402
    /**
403
     * Load settings from the resource relationship on to this.
404
     */
405
    protected function sync()
406
    {
407
        $this->settings = $this->getAllSettingsFlat();
408
    }
409
410
    /**
411
     * Get all settings as a flat collection.
412
     *
413
     * @return \Illuminate\Support\Collection
414
     */
415
    protected function getAllSettingsFlat()
416
    {
417
        return $this->getAllSettings()->flatMap(function (Model $model) {
418
            return [$model->key => json_decode($model->value)[0]];
419
        });
420
    }
421
422
    /**
423
     * Retrieve all settings from database.
424
     *
425
     * @return \Illuminate\Support\Collection
426
     */
427
    protected function getAllSettings()
428
    {
429
        if ($this->resource->relationLoaded('propertyBag')) {
430
            return $this->resource->propertyBag;
431
        }
432
433
        return $this->propertyBag()
434
            ->where('resource_id', $this->resource->getKey())
435
            ->get();
436
    }
437
438
    /**
439
     * Get value from settings by key. Get registered default if not set.
440
     *
441
     * @param string $key
442
     *
443
     * @return mixed
444
     */
445
    public function get($key)
446
    {
447
        return $this->allSaved()->get($key, function () use ($key) {
448
            return $this->getDefault($key);
449
        });
450
    }
451
}
452