Skip to content

Commit a771986

Browse files
committed
- Optimize settings retrieval with runtime and per-class config caching.
- Add `clearSettingsCache` method to handle clearing runtime and Redis cache. - Update README with Redis request counting instructions.
1 parent a0bfb7d commit a771986

2 files changed

Lines changed: 113 additions & 81 deletions

File tree

src/Managers/TableSettingsManager.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Glorand\Model\Settings\Contracts\SettingsManagerContract;
66
use Glorand\Model\Settings\Models\ModelSettings;
7+
use Illuminate\Support\Facades\Redis;
78

89
/**
910
* Class TableSettingsManager
@@ -16,29 +17,57 @@ class TableSettingsManager extends AbstractSettingsManager
1617
* @param array $settings
1718
* @return \Glorand\Model\Settings\Contracts\SettingsManagerContract
1819
* @throws \Exception
19-
* @SuppressWarnings(PHPMD.ElseExpression)
2020
*/
2121
public function apply(array $settings = []): SettingsManagerContract
2222
{
2323
$this->validate($settings);
2424

2525
$modelSettings = $this->model->modelSettings()->first();
26-
if (!count($settings)) {
26+
27+
if (! count($settings)) {
2728
if ($modelSettings) {
28-
$modelSettings->delete();
29+
$modelSettings->delete(); // fires deleted event → cache flushed
2930
}
3031
} else {
31-
if (!$modelSettings) {
32+
if (! $modelSettings) {
3233
$modelSettings = new ModelSettings();
3334
$modelSettings->setConnection($this->model->getConnectionName() ?? config('database.default'));
3435
$modelSettings->model()->associate($this->model);
3536
}
37+
3638
$modelSettings->settings = $settings;
37-
$modelSettings->save();
39+
$modelSettings->save(); // fires saved event → cache flushed
3840
}
3941

42+
// Optional: warm Redis cache instantly via pipeline
43+
$this->warmCache($settings);
44+
45+
$this->model->flushSettingsCache();
46+
4047
cache()->forget($this->model->getSettingsCacheKey());
4148

4249
return $this;
4350
}
44-
}
51+
52+
/* -----------------------------------------------------------------
53+
| Internal helpers
54+
| -----------------------------------------------------------------
55+
*/
56+
private function warmCache(array $settings): void
57+
{
58+
$cacheKey = $this->model->getSettingsCacheKey();
59+
$payload = $this->compress(json_encode($settings));
60+
61+
Redis::connection()->pipeline(function ($pipe) use ($cacheKey, $payload, $settings) {
62+
$pipe->del($cacheKey);
63+
if (count($settings)) {
64+
$pipe->set($cacheKey, $payload);
65+
}
66+
});
67+
}
68+
69+
private function compress(string $data): string
70+
{
71+
return gzencode($data, 6);
72+
}
73+
}

src/Traits/HasSettingsTable.php

Lines changed: 78 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -8,121 +8,124 @@
88
use Illuminate\Database\Eloquent\Relations\MorphOne;
99
use Illuminate\Support\Facades\Cache;
1010

11+
/**
12+
* Trait HasSettingsTable
13+
* @package Glorand\Model\Settings\Traits
14+
*
15+
* @property ModelSettings $modelSettings
16+
* @property array $settings
17+
* @method morphOne(string $related, string $name, string $type = null, string $id = null, string $localKey = null)
18+
*/
1119
trait HasSettingsTable
1220
{
1321
use HasSettings;
1422

15-
protected $settingsRuntimeCache = null;
16-
protected $settingsManagerInstance = null;
17-
protected static $configCache = [];
23+
/** @var SettingsManagerContract|null */
24+
private ?SettingsManagerContract $_settingsManager = null;
1825

19-
/**
20-
* Returns a settings manager instance for the model, creating one if necessary.
21-
* @return SettingsManagerContract
22-
* @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException
26+
/** @var array|null In-memory copy */
27+
private ?array $_settingsCache = null;
28+
29+
/* -----------------------------------------------------------------
30+
| Boot – register model event listeners
31+
| -----------------------------------------------------------------
2332
*/
24-
public function settings(): SettingsManagerContract
33+
public static function bootHasSettingsTable(): void
2534
{
26-
if ($this->settingsManagerInstance === null) {
27-
$this->settingsManagerInstance = new TableSettingsManager($this);
28-
}
35+
// When the polymorphic row changes, flush our caches
36+
static::saved(function ($model) {
37+
$model->flushSettingsCache();
38+
Cache::forget($model->getSettingsCacheKey());
39+
});
40+
41+
static::deleted(function ($model) {
42+
$model->flushSettingsCache();
43+
Cache::forget($model->getSettingsCacheKey());
44+
});
45+
}
2946

30-
return $this->settingsManagerInstance;
47+
/* -----------------------------------------------------------------
48+
| Settings Manager
49+
| -----------------------------------------------------------------
50+
*/
51+
public function settings(): SettingsManagerContract
52+
{
53+
return $this->_settingsManager ??= new TableSettingsManager($this);
3154
}
3255

33-
/**
34-
* Retrieves the settings value for the model, optionally using a cache.
35-
* @return array
56+
/* -----------------------------------------------------------------
57+
| Settings Value (lazy + in-memory)
58+
| -----------------------------------------------------------------
3659
*/
3760
public function getSettingsValue(): array
3861
{
39-
// Runtime cache = ZERO Redis calls after first access
40-
if ($this->settingsRuntimeCache !== null) {
41-
return $this->settingsRuntimeCache;
42-
}
43-
44-
// Cache config lookup once per class (not per instance)
45-
if (!isset(static::$configCache['use_cache'])) {
46-
static::$configCache['use_cache'] = config('model_settings.settings_table_use_cache');
62+
if ($this->_settingsCache !== null) {
63+
return $this->_settingsCache;
4764
}
4865

49-
if (static::$configCache['use_cache']) {
50-
// Only 1 Redis call per request
51-
$this->settingsRuntimeCache = Cache::rememberForever(
52-
$this->getSettingsCacheKey(),
53-
fn() => $this->__getSettingsValue()
54-
);
55-
} else {
56-
$this->settingsRuntimeCache = $this->__getSettingsValue();
66+
if (! config('model_settings.settings_table_use_cache')) {
67+
return $this->_settingsCache = $this->loadSettingsFromDatabase();
5768
}
5869

59-
return $this->settingsRuntimeCache;
70+
return $this->_settingsCache = Cache::rememberForever(
71+
$this->getSettingsCacheKey(),
72+
fn () => $this->loadSettingsFromDatabase()
73+
);
6074
}
6175

6276
/**
63-
* Retrieves the settings value for the model.
64-
* @return array
77+
* Reload settings and refresh all caches.
6578
*/
66-
private function __getSettingsValue(): array
79+
public function refreshSettings(): array
6780
{
68-
// Eager loading support (prevents N+1)
69-
if ($this->relationLoaded('modelSettings')) {
70-
$modelSettings = $this->getRelation('modelSettings');
71-
return $modelSettings ? $modelSettings->settings : [];
72-
}
73-
74-
$modelSettings = $this->modelSettings()
75-
->select('settings', 'model_id', 'model_type')
76-
->first();
81+
$this->flushSettingsCache();
82+
Cache::forget($this->getSettingsCacheKey());
7783

78-
return $modelSettings ? $modelSettings->settings : [];
84+
return $this->getSettingsValue();
7985
}
8086

8187
/**
82-
* Define an inverse one-to-one or many relationship.
83-
* @return MorphOne
88+
* Flush only the in-RAM copy.
8489
*/
85-
public function modelSettings(): MorphOne
90+
public function flushSettingsCache(): void
8691
{
87-
return $this->morphOne(ModelSettings::class, 'model');
92+
$this->_settingsCache = null;
8893
}
8994

9095
/**
91-
* Retrieves the cache key for settings associated with the model.
92-
*
93-
* This method ensures that a consistent cache key is generated for
94-
* the model's settings by utilizing a shared static configuration
95-
* for the cache prefix and combining it with the table name and
96-
* primary key of the current model instance.
97-
*
98-
* @return string The generated cache key for the model's settings.
96+
* Raw DB hit (no caching layer).
9997
*/
100-
public function getSettingsCacheKey(): string
98+
private function loadSettingsFromDatabase(): array
10199
{
102-
// Static config cache shared across all instances
103-
if (!isset(static::$configCache['cache_prefix'])) {
104-
static::$configCache['cache_prefix'] = config('model_settings.settings_table_cache_prefix');
105-
}
100+
/** @var ModelSettings|null $row */
101+
$row = $this->modelSettings()->first(['settings']);
106102

107-
return static::$configCache['cache_prefix'] . $this->getTable() . '::' . $this->getKey();
103+
return $row?->settings ?? [];
108104
}
109105

110-
/**
111-
* Clears the runtime cache and optionally the cache for the model's settings.'
112-
* @return void
106+
/* -----------------------------------------------------------------
107+
| Relations
108+
| -----------------------------------------------------------------
113109
*/
114-
public function clearSettingsCache(): void
110+
public function modelSettings(): MorphOne
115111
{
116-
$this->settingsRuntimeCache = null;
112+
return $this->morphOne(ModelSettings::class, 'model');
113+
}
117114

118-
if (static::$configCache['use_cache'] ?? config('model_settings.settings_table_use_cache')) {
119-
Cache::forget($this->getSettingsCacheKey());
120-
}
115+
116+
/* -----------------------------------------------------------------
117+
| Helpers
118+
| -----------------------------------------------------------------
119+
*/
120+
public function getSettingsCacheKey(): string
121+
{
122+
return config('model_settings.settings_table_cache_prefix')
123+
. $this->getTable() . '::' . $this->getKey();
121124
}
122125

123-
/**
124-
* Retrieves the name of the database table associated with the model.
125-
* @return string
126+
/* -----------------------------------------------------------------
127+
| Abstract requirements
128+
| -----------------------------------------------------------------
126129
*/
127130
abstract public function getTable();
128-
}
131+
}

0 commit comments

Comments
 (0)