Completed
Push — dev ( 6d32a8...3bb7a4 )
by Zach
02:22
created

Settings::createRecord()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 2
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;
0 ignored issues
show
Documentation Bug introduced by
It seems like $settingsConfig of type object<LaravelPropertyBa...ettings\ResourceConfig> is incompatible with the declared type object<LaravelPropertyBa...ettings\ResourceConfig> of property $settingsConfig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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