From 9af9e3a21aef8aa4eb0e74a1b3d2e23f35b516ea Mon Sep 17 00:00:00 2001 From: manuel-watchenterprise Date: Tue, 21 May 2024 14:17:01 +0200 Subject: [PATCH 1/3] feat(provider): move store registering to dedicated function --- src/NovaSettingsServiceProvider.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/NovaSettingsServiceProvider.php b/src/NovaSettingsServiceProvider.php index b614f1a..47e8860 100644 --- a/src/NovaSettingsServiceProvider.php +++ b/src/NovaSettingsServiceProvider.php @@ -46,6 +46,11 @@ public function register() 'nova-settings' ); + $this->registerSettingsStore(); + } + + protected function registerSettingsStore() + { $this->app->singleton(NovaSettingsStore::class, function () { return new NovaSettingsStore(); }); From 2ed78523fc429c2ab57bd598d031e663e67f6684 Mon Sep 17 00:00:00 2001 From: manuel-watchenterprise Date: Tue, 21 May 2024 16:34:12 +0200 Subject: [PATCH 2/3] feat(caching): introduce caching options --- README.md | 11 ++-- config/nova-settings.php | 19 ++++++- src/NovaSettingsCacheStore.php | 67 +++++++++++++++++++++++ src/NovaSettingsInMemoryStore.php | 41 ++++++++++++++ src/NovaSettingsNoCacheStore.php | 23 ++++++++ src/NovaSettingsServiceProvider.php | 26 +++++++-- src/NovaSettingsStore.php | 84 +++++++++++++++++------------ 7 files changed, 228 insertions(+), 43 deletions(-) create mode 100644 src/NovaSettingsCacheStore.php create mode 100644 src/NovaSettingsInMemoryStore.php create mode 100644 src/NovaSettingsNoCacheStore.php diff --git a/README.md b/README.md index c61c762..f22ffaa 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/config/nova-settings.php b/config/nova-settings.php index 61cfaa1..65e0d5b 100644 --- a/config/nova-settings.php +++ b/config/nova-settings.php @@ -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:', + ], ]; diff --git a/src/NovaSettingsCacheStore.php b/src/NovaSettingsCacheStore.php new file mode 100644 index 0000000..955adf4 --- /dev/null +++ b/src/NovaSettingsCacheStore.php @@ -0,0 +1,67 @@ +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; + } +} diff --git a/src/NovaSettingsInMemoryStore.php b/src/NovaSettingsInMemoryStore.php new file mode 100644 index 0000000..5005fbd --- /dev/null +++ b/src/NovaSettingsInMemoryStore.php @@ -0,0 +1,41 @@ +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; + } +} diff --git a/src/NovaSettingsNoCacheStore.php b/src/NovaSettingsNoCacheStore.php new file mode 100644 index 0000000..283d4d9 --- /dev/null +++ b/src/NovaSettingsNoCacheStore.php @@ -0,0 +1,23 @@ +app->singleton(NovaSettingsStore::class, function () { - return new NovaSettingsStore(); - }); + $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() diff --git a/src/NovaSettingsStore.php b/src/NovaSettingsStore.php index 6e4429e..f002447 100644 --- a/src/NovaSettingsStore.php +++ b/src/NovaSettingsStore.php @@ -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 = []; @@ -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; @@ -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(); } } From c7d4a5dcfbdb630237efc25dea1977d2ee716c0f Mon Sep 17 00:00:00 2001 From: manuel-watchenterprise Date: Wed, 22 May 2024 10:27:43 +0200 Subject: [PATCH 3/3] feat(caching): fix caching values --- src/NovaSettingsStore.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NovaSettingsStore.php b/src/NovaSettingsStore.php index f002447..e1e6216 100644 --- a/src/NovaSettingsStore.php +++ b/src/NovaSettingsStore.php @@ -99,8 +99,10 @@ public function getSettings(array $settingKeys = null, array $defaults = []) } return $this->getSettingsModelClass()::all() - ->tap(function ($setting) { - $this->setCached($setting->key, $setting->value); + ->tap(function ($settings) { + $settings->each(function ($setting) { + $this->setCached($setting->key, $setting->value); + }); }) ->pluck('value', 'key') ->toArray();