diff --git a/config/settings.php b/config/settings.php index a109414..00fd427 100644 --- a/config/settings.php +++ b/config/settings.php @@ -148,4 +148,19 @@ | */ 'key_generator' => \Rawilk\Settings\Support\KeyGenerators\Md5KeyGenerator::class, + + /* + |-------------------------------------------------------------------------- + | Value Serializer + |-------------------------------------------------------------------------- + | + | By default, we use php's serialize() and unserialize() functions to + | prepare the setting values for storage. You may use the `JsonValueSerializer` + | instead if you want to store the values as json instead. + | + | Any custom value serializer you use must implement the + | \Rawilk\Settings\Contracts\ValueSerializer interface. + | + */ + 'value_serializer' => \Rawilk\Settings\Support\ValueSerializers\ValueSerializer::class, ]; diff --git a/src/Contracts/ValueSerializer.php b/src/Contracts/ValueSerializer.php new file mode 100644 index 0000000..1693bf6 --- /dev/null +++ b/src/Contracts/ValueSerializer.php @@ -0,0 +1,12 @@ +valueSerializer = new ValueSerializer; } // mainly for testing purposes diff --git a/src/SettingsServiceProvider.php b/src/SettingsServiceProvider.php index cc4d1f4..c201959 100644 --- a/src/SettingsServiceProvider.php +++ b/src/SettingsServiceProvider.php @@ -8,6 +8,7 @@ use Rawilk\Settings\Drivers\Factory; use Rawilk\Settings\Support\ContextSerializers\ContextSerializer; use Rawilk\Settings\Support\KeyGenerators\Md5KeyGenerator; +use Rawilk\Settings\Support\ValueSerializers\ValueSerializer; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -61,14 +62,15 @@ protected function registerSettings(): void ); $this->app->singleton(Settings::class, function ($app) { - $keyGenerator = $this->app->make($app['config']['settings.key_generator'] ?? Md5KeyGenerator::class); + $keyGenerator = $app->make($app['config']['settings.key_generator'] ?? Md5KeyGenerator::class); $keyGenerator->setContextSerializer( - $this->app->make($app['config']['settings.context_serializer'] ?? ContextSerializer::class) + $app->make($app['config']['settings.context_serializer'] ?? ContextSerializer::class) ); $settings = new Settings( - $app['SettingsFactory']->driver(), - $keyGenerator, + driver: $app['SettingsFactory']->driver(), + keyGenerator: $keyGenerator, + valueSerializer: $app->make($app['config']['settings.value_serializer'] ?? ValueSerializer::class), ); $settings->useCacheKeyPrefix($app['config']['settings.cache_key_prefix'] ?? ''); diff --git a/src/Support/ValueSerializers/JsonValueSerializer.php b/src/Support/ValueSerializers/JsonValueSerializer.php new file mode 100644 index 0000000..ba404fe --- /dev/null +++ b/src/Support/ValueSerializers/JsonValueSerializer.php @@ -0,0 +1,20 @@ +and(SettingsFacade::isFalse('app.debug'))->toBeFalse(); }); +it('can evaluate boolean stored settings using the json value serializer', function () { + $settings = settings(); + (fn () => $this->valueSerializer = new JsonValueSerializer)->call($settings); + + $settings->set('app.debug', '1'); + expect($settings->isTrue('app.debug'))->toBeTrue(); + + $settings->set('app.debug', '0'); + expect($settings->isTrue('app.debug'))->toBeFalse() + ->and($settings->isFalse('app.debug'))->toBeTrue(); + + $settings->set('app.debug', true); + expect($settings->isTrue('app.debug'))->toBeTrue() + ->and($settings->isFalse('app.debug'))->toBeFalse(); +}); + it('can cache values on retrieval', function () { enableSettingsCache(); @@ -264,6 +281,28 @@ ->not->toBe(serialize('bar')); }); +test('custom value serializers can be used', function () { + $settings = settings(); + (fn () => $this->valueSerializer = new JsonValueSerializer)->call($settings); + + $settings->disableEncryption(); + + $settings->set('foo', 'my value'); + $settings->set('array-value', ['foo' => 'bar', 'bool' => true]); + + $this->assertDatabaseHas('settings', [ + 'value' => '"my value"', + ]); + + $this->assertDatabaseHas('settings', [ + 'value' => '{"foo":"bar","bool":true}', + ]); + + expect($settings->get('foo'))->toBe('my value') + ->and($settings->get('array-value'))->toBeArray() + ->and($settings->get('array-value'))->toMatchArray(['foo' => 'bar', 'bool' => true]); +}); + // Helpers... function assertQueryCount(int $expected): void diff --git a/tests/Unit/ValueSerializers/JsonValueSerializerTest.php b/tests/Unit/ValueSerializers/JsonValueSerializerTest.php new file mode 100644 index 0000000..3967d21 --- /dev/null +++ b/tests/Unit/ValueSerializers/JsonValueSerializerTest.php @@ -0,0 +1,38 @@ +serializer = new JsonValueSerializer; +}); + +it('serializes values as json', function (mixed $value, string $expected) { + expect($this->serializer->serialize($value))->toBe($expected); +})->with([ + [1, '1'], + ['1', '"1"'], + [true, 'true'], + [false, 'false'], + [0, '0'], + [null, 'null'], + [1.1, '1.1'], + [['array' => 'array'], '{"array":"array"}'], + [[1, '2', 3], '[1,"2",3]'], + [(object) ['a' => 'b'], '{"a":"b"}'], +]); + +it('unserializes json values', function (string $value, mixed $expected) { + expect($this->serializer->unserialize($value))->toBe($expected); +})->with([ + ['1', 1], + ['"1"', '1'], + ['true', true], + ['false', false], + ['0', 0], + ['null', null], + ['1.1', 1.1], + ['{"array":"array"}', ['array' => 'array']], + ['{"a":"b"}', ['a' => 'b']], +]); diff --git a/tests/Unit/ValueSerializerTest.php b/tests/Unit/ValueSerializers/ValueSerializerTest.php similarity index 91% rename from tests/Unit/ValueSerializerTest.php rename to tests/Unit/ValueSerializers/ValueSerializerTest.php index 44cb732..dfaad53 100644 --- a/tests/Unit/ValueSerializerTest.php +++ b/tests/Unit/ValueSerializers/ValueSerializerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use Rawilk\Settings\Support\ValueSerializer; +use Rawilk\Settings\Support\ValueSerializers\ValueSerializer; it('serializes values', function (mixed $value) { $serializer = new ValueSerializer;