Skip to content
This repository has been archived by the owner on Feb 18, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1 from DarkGhostHunter/master
Browse files Browse the repository at this point in the history
Easier braindead get/set
  • Loading branch information
DarkGhostHunter authored Jul 18, 2021
2 parents 4a368dc + 53052d5 commit 6da6be6
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 22 deletions.
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![Xavier von Erlach - Unsplash #ooR1jY2yFr4](https://images.unsplash.com/photo-1570221622224-3bb8f08f166c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1200&h=400&q=80)

[![Latest Version on Packagist](https://img.shields.io/packagist/v/darkghosthunter/laraconfig.svg?style=flat-square)](https://packagist.org/packages/darkghosthunter/laraconfig) [![License](https://poser.pugx.org/darkghosthunter/laraconfig/license)](https://packagist.org/packages/darkghosthunter/laraconfig) ![](https://img.shields.io/packagist/php-v/darkghosthunter/laraconfig.svg) ![](https://github.com/DarkGhostHunter/Laraconfig/workflows/PHP%20Composer/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/Laraconfig/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/Laraconfig?branch=master) [![Laravel Octane Compatible](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://github.com/laravel/octane)
[![Latest Version on Packagist](https://img.shields.io/packagist/v/darkghosthunter/laraconfig.svg)](https://packagist.org/packages/darkghosthunter/laraconfig) [![License](https://poser.pugx.org/darkghosthunter/laraconfig/license)](https://packagist.org/packages/darkghosthunter/laraconfig) ![](https://img.shields.io/packagist/php-v/darkghosthunter/laraconfig.svg) ![](https://github.com/DarkGhostHunter/Laraconfig/workflows/PHP%20Composer/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/Laraconfig/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/Laraconfig?branch=master) [![Laravel Octane Compatible](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://github.com/laravel/octane)

# Laraconfig

Expand Down Expand Up @@ -313,7 +313,19 @@ if ($user->config()->isInitialized()) {

### Retrieving settings

You can use `get()` with the name of the setting to get, which saves you a few lines. If the setting doesn't exist, it will return `null`.
You can easily get a value of a setting using the name, which makes everything into a single beautiful _oneliner_.

```php
return "Your favorite color is {$user->settings->color}";
```

Since this only supports alphanumeric and underscore characters, you can use `value()`.

```php
return "Your favorite color is {$user->settings->value('color')}";
```

You can also get the underlying Setting model using `get()`. If the setting doesn't exist, it will return `null`.

```php
$setting = $user->settings->get('theme');
Expand Down Expand Up @@ -349,10 +361,16 @@ $user->settings->groups(); // or ->groupBy('group')
### Setting a value

Setting a value can be easily done using `set()` with the name of the setting and the value.
Setting a value can be easily done by issuing the name of the setting and the value.

```php
$user->settings->color = 'red';
```

Since this only supports settings with names made of alphanumeric and underscores, you can also set a value using the `set()` method by issuing the name of the setting.

```php
$user->settings->set('color', 'red');
$user->settings->set('color-default', 'red');
```

Or, you can go the purist mode directly in the model itself.
Expand All @@ -364,7 +382,7 @@ $setting->value = 'red';
$setting->save();
```

You can also set multiple settings using an array when using `set()`.
You can also set multiple settings using an array when using `set()` in one go, which is useful when dealing with the [array returned by a validation](https://laravel.com/docs/validation#quick-writing-the-validation-logic).

```php
$user->settings->set([
Expand Down Expand Up @@ -507,15 +525,15 @@ public function store(Request $request, User $user)
'color' => 'required|string|in:red,green,blue'
]);

$user->settings->set($settings);
$user->settings->setIfEnabled($settings);

// ...
}
```

## Testing

Eventually you will land into the problem of creating settings and metadata for each user created. You can easily create Metadata directly into the database.
Eventually you will land into the problem of creating settings and metadata for each user created. You can easily create Metadata directly into the database _before_ creating a user, unless you have disabled [initialization](#initializing).

```php
public function test_user_has_settings(): void
Expand All @@ -528,6 +546,10 @@ public function test_user_has_settings(): void
'group' => 'default',
]);

$user = User::create([
// ...
]);

// ...
}
```
Expand Down
11 changes: 5 additions & 6 deletions src/SettingsCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,13 @@ public function retrieve(): ?Collection
*/
public function shouldRegenerate(): bool
{
// If wasn't invalidated before, or there is no cache for the last
// invalidation timestamp, then we should invalidate the data. If
// there is a timestamp we will check if we are not in the past.
if (! $this->invalidatedAt) {
// If the time doesn't exist in the cache then we can safely store.
if (!$time = $this->cache->get("$this->key:time")) {
return true;
}

return $this->invalidatedAt->isAfter($this->cache->get("$this->key:time"));
// Return if the is an invalidation (data changed) and is fresher.
return (bool) $this->invalidatedAt?->isAfter($time);
}

/**
Expand Down Expand Up @@ -181,4 +180,4 @@ public static function make(Config $config, Factory $factory, Model $model): sta
$config->get('laraconfig.cache.automatic', true),
);
}
}
}
62 changes: 56 additions & 6 deletions src/SettingsCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@

namespace DarkGhostHunter\Laraconfig;

use DarkGhostHunter\Laraconfig\Eloquent\Setting;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Enumerable;
use Illuminate\Support\Traits\EnumeratesValues;
use RuntimeException;

/**
* Class SettingsCollection
*
* @package DarkGhostHunter\Laraconfig
*
* @method \DarkGhostHunter\Laraconfig\Eloquent\Setting get(string $name, mixed $default = null)
* @method Setting get(string $name, mixed $default = null)
*/
class SettingsCollection extends Collection
{
use EnumeratesValues {
__get as __dynamicGet;
}
/**
* The cache helper instance.
*
Expand Down Expand Up @@ -93,7 +99,11 @@ public function set(string|array $name, mixed $value, bool $force = true): void
}

foreach ($name as $key => $setting) {
$this->get($key)->set($setting, $force);
if (! $instance = $this->get($key)) {
throw new RuntimeException("The setting [$key] doesn't exist.");
}

$instance->set($setting, $force);
}
}

Expand Down Expand Up @@ -172,14 +182,14 @@ public function enable(string $name): void
/**
* Sets a value into a setting if it exists and it's enabled.
*
* @param string $name
* @param string|array $name
* @param mixed $value
*
* @return void
*/
public function setIfEnabled(string $name, mixed $value): void
public function setIfEnabled(string|array $name, mixed $value = null): void
{
$this->get($name)->setIfEnabled($value);
$this->set($name, $value, false);
}

/**
Expand Down Expand Up @@ -275,4 +285,44 @@ public function __destruct()
$this->cache?->setSettings($this)->regenerate();
}
}
}

/**
* Dynamically sets a value.
*
* @param string $name
* @param mixed $value
*/
public function __set(string $name, mixed $value): void
{
$this->set($name, $value);
}

/**
* Check if a given property exists.
*
* @param string $name
*
* @return bool
*/
public function __isset(string $name): bool
{
return $this->has($name);
}

/**
* Dynamically access collection proxies.
*
* @param string $key
* @return mixed
*
* @throws \Exception
*/
public function __get($key): mixed
{
if ($setting = $this->get($key)) {
return $setting->getAttribute('value');
}

return $this->__dynamicget($key);
}
}
114 changes: 111 additions & 3 deletions tests/HasConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
use DarkGhostHunter\Laraconfig\Eloquent\Setting;
use DarkGhostHunter\Laraconfig\HasConfig;
use DarkGhostHunter\Laraconfig\SettingsCollection;
use Error;
use Exception;
use Illuminate\Contracts\Cache\Factory;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\HigherOrderCollectionProxy;
use Mockery;
use RuntimeException;
use Tests\Dummies\DummyModel;

Expand Down Expand Up @@ -177,13 +181,26 @@ public function test_model_sets_config_not_forcefully(): void
$user = DummyModel::find(1);

$user->settings->disable('foo');
$user->settings->set('foo', 'quz', false);

$user->settings->set('foo', 'quz', false);
static::assertEquals('bar', $user->settings->value('foo'));

$user->settings->setIfEnabled('foo', 'quz');
static::assertEquals('bar', $user->settings->value('foo'));

$user->settings->setIfEnabled(['foo' => 'quz']);
static::assertEquals('bar', $user->settings->value('foo'));

$user->settings->enable('foo');

$user->settings->set('foo', 'quz', false);
static::assertEquals('quz', $user->settings->value('foo'));

$user->settings->setIfEnabled('foo', 'bar');
static::assertEquals('bar', $user->settings->value('foo'));

$user->settings->setIfEnabled(['foo' => 'quz']);
static::assertEquals('quz', $user->settings->value('foo'));
}

public function test_model_gets_config(): void
Expand Down Expand Up @@ -475,8 +492,12 @@ public function test_should_regenerate_cache_if_cache_enabled(): void
'name' => 'foo',
])]));

$cache->shouldReceive('get')
->with('laraconfig|'.DummyModel::class.'|1:time')
->andReturn(now()->subMinute());

$cache->shouldReceive('set')
->with('laraconfig|'.DummyModel::class.'|1', \Mockery::type(Collection::class), 60 * 60 * 3)
->with('laraconfig|'.DummyModel::class.'|1', Mockery::type(Collection::class), 60 * 60 * 3)
->andReturns();

$cache->shouldReceive('setMultiple')
Expand All @@ -495,6 +516,40 @@ public function test_should_regenerate_cache_if_cache_enabled(): void
static::assertFalse($user->settings->regeneratesOnExit);
}

public function test_should_not_regenerate_cache_if_is_not_fresher(): void
{
config()->set('laraconfig.cache.enable', true);

$cache = $this->mock(Repository::class);

$this->mock(Factory::class)
->shouldReceive('store')
->with(null)
->andReturn($cache);

$cache->shouldReceive('forget')
->with('laraconfig|'.DummyModel::class.'|1');

$cache->shouldReceive('get')
->with('laraconfig|'.DummyModel::class.'|1')
->andReturn(new SettingsCollection([(new Setting())->forceFill([
'name' => 'foo',
])]));

$cache->shouldReceive('get')
->with('laraconfig|'.DummyModel::class.'|1:time')
->andReturn(now()->addMinute());

$cache->shouldNotReceive('set');
$cache->shouldNotReceive('setMultiple');

$user = DummyModel::find(1);

$user->settings->regenerate();

static::assertFalse($user->settings->regeneratesOnExit);
}

public function test_doesnt_invalidates_cache_on_save_if_cache_disabled(): void
{
$this->mock(Factory::class)->shouldNotReceive('store');
Expand Down Expand Up @@ -747,4 +802,57 @@ public function test_cache_avoids_data_races(): void

static::assertEquals('qux', cache()->get('laraconfig|'.DummyModel::class.'|1')->get('foo')->value);
}
}

public function test_checks_settings_has_key(): void
{
$user = DummyModel::find(1);

static::assertTrue(isset($user->settings->foo));
static::assertFalse(isset($user->settings->bar));
}

public function test_sets_value_dynamically(): void
{
$user = DummyModel::find(1);

$user->settings->foo = 'quz';

$this->assertDatabaseHas('user_settings', ['id' => 1, 'value' => 'quz']);
}

public function test_sets_doesnt_sets_property_dynamically_into_collection(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage("The setting [invalid] doesn't exist.");

$user = DummyModel::find(1);

$user->settings->invalid = 'quz';

$user->settings->invalid;
}

public function test_gets_value_dynamically(): void
{
$user = DummyModel::find(1);

static::assertEquals('bar', $user->settings->foo);
}

public function test_exception_when_dynamic_get_doesnt_exists(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('Property [invalid] does not exist on this collection instance');

$user = DummyModel::find(1);

$user->settings->invalid;
}

public function test_get_allows_pass_to_higher_order_proxy(): void
{
$user = DummyModel::find(1);

static::assertInstanceOf(HigherOrderCollectionProxy::class, $user->settings->map);
}
}

0 comments on commit 6da6be6

Please sign in to comment.