Skip to content

Commit

Permalink
Merge pull request #1 from watchenterprise/cache-resolution
Browse files Browse the repository at this point in the history
Cache resolution
  • Loading branch information
manuel-watchenterprise authored and Tarpsvo committed Dec 18, 2024
1 parent f9678fc commit 23579c7
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 43 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,12 @@ The config file can be published using the following command:
php artisan vendor:publish --provider="Outl1ne\NovaSettings\NovaSettingsServiceProvider" --tag="config"
```

| Name | Type | Default | Description |
| --------------------- | ------- | ----------------- | ---------------------------------------------------------------------------------- |
| `base_path` | String | `nova-settings` | URL path of settings page. |
| `reload_page_on_save` | Boolean | false | Reload the entire page on save. Useful when updating any Nova UI related settings. |
| `models.settings` | Model | `Settings::class` | Optionally override the Settings model. |
| Name | Type | Default | Description |
|-----------------------|---------|-------------------|--------------------------------------------------------------------------------------------------|
| `base_path` | String | `nova-settings` | URL path of settings page. |
| `reload_page_on_save` | Boolean | false | Reload the entire page on save. Useful when updating any Nova UI related settings. |
| `models.settings` | Model | `Settings::class` | Optionally override the Settings model. |
| `cache` | String | `:memory:` | Cache store name to use that cache, ":memory:" for singleton class, or null to turn off caching. |

The migration can also be published and overwritten using:

Expand Down
19 changes: 18 additions & 1 deletion config/nova-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,22 @@
/**
* Show the sidebar menu
*/
'show_in_sidebar' => true
'show_in_sidebar' => true,

/*
|--------------------------------------------------------------------------
| Cache settings
|--------------------------------------------------------------------------
|
| Here you may specify which of the cache connection should be used to
| cache the settings. `:memory:` is the default which is a simple
| in-memory cache through a singleton service class property.
| `null` will disable caching.
|
*/
'cache' => [
'store' => env('NOVA_SETTINGS_CACHE_DRIVER', ':memory:'),

'prefix' => 'nova-settings:',
],
];
67 changes: 67 additions & 0 deletions src/NovaSettingsCacheStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Outl1ne\NovaSettings;

use Illuminate\Support\Facades\Cache;

use function collect;
use function is_array;
use function is_string;

class NovaSettingsCacheStore extends NovaSettingsStore
{
/** @var \Illuminate\Contracts\Cache\Repository */
private $cache;

public function __construct()
{
$this->cache = Cache::store(config('nova-settings.cache.store'));
}

public function clearCache($keyNames = null)
{
// Clear whole cache
if (empty($keyNames)) {
$this->getSettingsModelClass()::all(['key'])->each(function ($setting) {
$this->cache->forget($this->getCacheKey($setting->key));
});

return;
}

// Clear specific keys
if (is_string($keyNames)) $keyNames = [$keyNames];
foreach ($keyNames as $key) {
$this->cache->forget($this->getCacheKey($key));
}
}

protected function getCached($keyNames = null)
{
if (is_string($keyNames)) {
return $this->cache->get($this->getCacheKey($keyNames));
}

if (is_array($keyNames)) {
return collect($keyNames)
->mapWithKeys(function ($key) {
if (!$this->cache->has($this->getCacheKey($key))) return [];

return [$key => $this->getCached($key)];
})
->toArray();
}

return [];
}

protected function setCached($keyName, $value)
{
$this->cache->forever($this->getCacheKey($keyName), $value);
}

private function getCacheKey($key)
{
return config('nova-settings.cache.prefix', 'nova-settings:') . $key;
}
}
41 changes: 41 additions & 0 deletions src/NovaSettingsInMemoryStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Outl1ne\NovaSettings;

use function collect;
use function is_array;
use function is_string;

class NovaSettingsInMemoryStore extends NovaSettingsStore
{
protected $cache = [];

public function clearCache($keyNames = null)
{
// Clear whole cache
if (empty($keyNames)) {
$this->cache = [];
return;
}

// Clear specific keys
if (is_string($keyNames)) $keyNames = [$keyNames];
foreach ($keyNames as $key) {
unset($this->cache[$key]);
}
}

protected function getCached($keyNames = null)
{
if (is_string($keyNames)) return $this->cache[$keyNames] ?? null;

return is_array($keyNames) && !empty($keyNames)
? collect($this->cache)->only($keyNames)->toArray()
: $this->cache;
}

protected function setCached($keyName, $value)
{
$this->cache[$keyName] = $value;
}
}
23 changes: 23 additions & 0 deletions src/NovaSettingsNoCacheStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Outl1ne\NovaSettings;

use function is_string;

class NovaSettingsNoCacheStore extends NovaSettingsStore
{
public function clearCache($keyNames = null)
{
}

protected function getCached($keyNames = null)
{
if (is_string($keyNames)) return null;

return [];
}

protected function setCached($keyName, $value)
{
}
}
31 changes: 28 additions & 3 deletions src/NovaSettingsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
use Outl1ne\NovaTranslationsLoader\LoadsNovaTranslations;
use Outl1ne\NovaSettings\Http\Middleware\SettingsPathExists;

use function array_keys;
use function config;
use function config_path;
use function database_path;
use function in_array;
use function inertia;
use function is_array;

class NovaSettingsServiceProvider extends ServiceProvider
{
use LoadsNovaTranslations;
Expand Down Expand Up @@ -46,9 +54,26 @@ public function register()
'nova-settings'
);

$this->app->singleton(NovaSettingsStore::class, function () {
return new NovaSettingsStore();
});
$this->registerSettingsStore();
}

protected function registerSettingsStore()
{
$caching = config('nova-settings.cache.store');

if (is_array(config('cache.stores')) && in_array($caching, array_keys(config('cache.stores')))) {
$this->app->singleton(NovaSettingsStore::class, function () {
return new NovaSettingsCacheStore();
});
} else if ($caching === ':memory:') {
$this->app->singleton(NovaSettingsStore::class, function () {
return new NovaSettingsInMemoryStore();
});
} else {
$this->app->singleton(NovaSettingsStore::class, function () {
return new NovaSettingsNoCacheStore();
});
}
}

protected function registerRoutes()
Expand Down
84 changes: 50 additions & 34 deletions src/NovaSettingsStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@

use Illuminate\Support\Str;

class NovaSettingsStore
use function array_diff;
use function array_keys;
use function array_map;
use function array_merge;
use function call_user_func;
use function collect;
use function is_array;
use function is_callable;

abstract class NovaSettingsStore
{
protected $cache = [];
protected $fields = [];
protected $casts = [];

Expand Down Expand Up @@ -55,29 +63,33 @@ public function getCasts()

public function getSetting($settingKey, $default = null)
{
if (isset($this->cache[$settingKey])) return $this->cache[$settingKey];
$this->cache[$settingKey] = NovaSettings::getSettingsModel()::getValueForKey($settingKey) ?? $default;
return $this->cache[$settingKey];
if ($cached = $this->getCached($settingKey)) return $cached;

$settingValue = $this->getSettingsModelClass()::getValueForKey($settingKey) ?? $default;

$this->setCached($settingKey, $settingValue);

return $settingValue;
}

public function getSettings(array $settingKeys = null, array $defaults = [])
{
$settingsModel = NovaSettings::getSettingsModel();

if (!empty($settingKeys)) {
$hasMissingKeys = !empty(array_diff($settingKeys, array_keys($this->cache)));
$cached = $this->getCached($settingKeys);

if (!$hasMissingKeys) return collect($settingKeys)->mapWithKeys(function ($settingKey) {
return [$settingKey => $this->cache[$settingKey]];
})->toArray();
$hasMissingKeys = !empty(array_diff($settingKeys, array_keys($cached)));

$settings = $settingsModel::whereIn('key', $settingKeys)->get()->pluck('value', 'key');
if (!$hasMissingKeys) return $cached;

$settings = $this->getSettingsModelClass()::whereIn('key', $settingKeys)
->get()
->pluck('value', 'key');

return collect($settingKeys)->flatMap(function ($settingKey) use ($settings, $defaults) {
$settingValue = $settings[$settingKey] ?? null;

if (isset($settingValue)) {
$this->cache[$settingKey] = $settingValue;
$this->setCached($settingKey, $settingValue);
return [$settingKey => $settingValue];
} else {
$defaultValue = $defaults[$settingKey] ?? null;
Expand All @@ -86,40 +98,44 @@ public function getSettings(array $settingKeys = null, array $defaults = [])
})->toArray();
}

return $settingsModel::all()->map(function ($setting) {
$this->cache[$setting->key] = $setting->value;
return $setting;
})->pluck('value', 'key')->toArray();
return $this->getSettingsModelClass()::all()
->tap(function ($setting) {
$this->setCached($setting->key, $setting->value);
})
->pluck('value', 'key')
->toArray();
}

public function setSettingValue($settingKey, $value = null)
{
$setting = NovaSettings::getSettingsModel()::firstOrCreate(['key' => $settingKey]);
$setting = $this->getSettingsModelClass()::firstOrCreate(['key' => $settingKey]);
$setting->value = $value;
$setting->save();
unset($this->cache[$settingKey]);
return $setting;
}

public function clearCache($keyNames = null)
{
// Clear whole cache
if (empty($keyNames)) {
$this->cache = [];
return;
}
$this->setCached($settingKey, $setting->value);

// Clear specific keys
if (is_string($keyNames)) $keyNames = [$keyNames];
foreach ($keyNames as $key) {
unset($this->cache[$key]);
}
return $setting;
}

public abstract function clearCache($keyNames = null);

public function clearFields()
{
$this->fields = [];
$this->casts = [];
$this->cache = [];

$this->clearCache();
}

protected abstract function getCached($keyNames = null);

protected abstract function setCached($keyName, $value);

/**
* @return class-string<\Outl1ne\NovaSettings\Models\Settings>
*/
protected function getSettingsModelClass()
{
return NovaSettings::getSettingsModel();
}
}

0 comments on commit 23579c7

Please sign in to comment.