Skip to content

Commit

Permalink
Add custom value serializer support (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
rawilk authored Sep 28, 2023
1 parent 795b9f4 commit b64ccc4
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 11 deletions.
15 changes: 15 additions & 0 deletions config/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
];
12 changes: 12 additions & 0 deletions src/Contracts/ValueSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Rawilk\Settings\Contracts;

interface ValueSerializer
{
public function serialize($value): string;

public function unserialize(string $serialized): mixed;
}
6 changes: 2 additions & 4 deletions src/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
use Illuminate\Support\Traits\Macroable;
use Rawilk\Settings\Contracts\Driver;
use Rawilk\Settings\Contracts\KeyGenerator;
use Rawilk\Settings\Contracts\ValueSerializer;
use Rawilk\Settings\Support\Context;
use Rawilk\Settings\Support\ValueSerializer;

class Settings
{
Expand All @@ -24,8 +24,6 @@ class Settings

protected ?Encrypter $encrypter = null;

protected ValueSerializer $valueSerializer;

protected bool $cacheEnabled = false;

protected bool $encryptionEnabled = false;
Expand All @@ -47,8 +45,8 @@ class Settings
public function __construct(
protected Driver $driver,
protected KeyGenerator $keyGenerator,
protected ValueSerializer $valueSerializer,
) {
$this->valueSerializer = new ValueSerializer;
}

// mainly for testing purposes
Expand Down
10 changes: 6 additions & 4 deletions src/SettingsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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'] ?? '');
Expand Down
20 changes: 20 additions & 0 deletions src/Support/ValueSerializers/JsonValueSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Rawilk\Settings\Support\ValueSerializers;

use Rawilk\Settings\Contracts\ValueSerializer as ValueSerializerContract;

class JsonValueSerializer implements ValueSerializerContract
{
public function serialize($value): string
{
return json_encode($value, JSON_THROW_ON_ERROR);
}

public function unserialize(string $serialized): mixed
{
return json_decode($serialized, true, 512, JSON_THROW_ON_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

declare(strict_types=1);

namespace Rawilk\Settings\Support;
namespace Rawilk\Settings\Support\ValueSerializers;

class ValueSerializer
use Rawilk\Settings\Contracts\ValueSerializer as ValueSerializerContract;

class ValueSerializer implements ValueSerializerContract
{
public function serialize($value): string
{
Expand Down
39 changes: 39 additions & 0 deletions tests/Feature/SettingsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\Facades\DB;
use Rawilk\Settings\Facades\Settings as SettingsFacade;
use Rawilk\Settings\Support\Context;
use Rawilk\Settings\Support\ValueSerializers\JsonValueSerializer;

beforeEach(function () {
config([
Expand Down Expand Up @@ -129,6 +130,22 @@
->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();

Expand Down Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions tests/Unit/ValueSerializers/JsonValueSerializerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

use Rawilk\Settings\Support\ValueSerializers\JsonValueSerializer;

beforeEach(function () {
$this->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']],
]);
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit b64ccc4

Please sign in to comment.