Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@ class User extends Model
}

```
## Counting redis requests
```
docker compose exec redis redis-cli monitor | grep -E '"(GET|SET|DEL)"' | grep "model_settings" | awk '{
if ($0 ~ /"GET"/) get++;
else if ($0 ~ /"SET"/) set++;
else if ($0 ~ /"DEL"/) del++;
total++;
printf "\rGET: %d, SET: %d, DEL: %d, TOTAL: %d", get, set, del, total;
}'
```



## Changelog <a name="changelog"></a>
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
Expand Down
41 changes: 35 additions & 6 deletions src/Managers/TableSettingsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Glorand\Model\Settings\Contracts\SettingsManagerContract;
use Glorand\Model\Settings\Models\ModelSettings;
use Illuminate\Support\Facades\Redis;

/**
* Class TableSettingsManager
Expand All @@ -16,29 +17,57 @@ class TableSettingsManager extends AbstractSettingsManager
* @param array $settings
* @return \Glorand\Model\Settings\Contracts\SettingsManagerContract
* @throws \Exception
* @SuppressWarnings(PHPMD.ElseExpression)
*/
public function apply(array $settings = []): SettingsManagerContract
{
$this->validate($settings);

$modelSettings = $this->model->modelSettings()->first();
if (!count($settings)) {

if (! count($settings)) {
if ($modelSettings) {
$modelSettings->delete();
$modelSettings->delete(); // fires deleted event → cache flushed
}
} else {
if (!$modelSettings) {
if (! $modelSettings) {
$modelSettings = new ModelSettings();
$modelSettings->setConnection($this->model->getConnectionName() ?? config('database.default'));
$modelSettings->model()->associate($this->model);
}

$modelSettings->settings = $settings;
$modelSettings->save();
$modelSettings->save(); // fires saved event → cache flushed
}

// Optional: warm Redis cache instantly via pipeline
$this->warmCache($settings);

$this->model->flushSettingsCache();

cache()->forget($this->model->getSettingsCacheKey());

return $this;
}
}

/* -----------------------------------------------------------------
| Internal helpers
| -----------------------------------------------------------------
*/
private function warmCache(array $settings): void
{
$cacheKey = $this->model->getSettingsCacheKey();
$payload = $this->compress(json_encode($settings));

Redis::connection()->pipeline(function ($pipe) use ($cacheKey, $payload, $settings) {
$pipe->del($cacheKey);
if (count($settings)) {
$pipe->set($cacheKey, $payload);
}
});
}

private function compress(string $data): string
{
return gzencode($data, 6);
}
}
103 changes: 83 additions & 20 deletions src/Traits/HasSettingsTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,58 +11,121 @@
/**
* Trait HasSettingsTable
* @package Glorand\Model\Settings\Traits
*
* @property ModelSettings $modelSettings
* @property array $settings
* @method morphOne($model, $name)
* @method morphOne(string $related, string $name, string $type = null, string $id = null, string $localKey = null)
*/
trait HasSettingsTable
{
use HasSettings;

/**
* @return \Glorand\Model\Settings\Contracts\SettingsManagerContract
* @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException
/** @var SettingsManagerContract|null */
private ?SettingsManagerContract $_settingsManager = null;

/** @var array|null In-memory copy */
private ?array $_settingsCache = null;

/* -----------------------------------------------------------------
| Boot – register model event listeners
| -----------------------------------------------------------------
*/
public static function bootHasSettingsTable(): void
{
// When the polymorphic row changes, flush our caches
static::saved(function ($model) {
$model->flushSettingsCache();
Cache::forget($model->getSettingsCacheKey());
});

static::deleted(function ($model) {
$model->flushSettingsCache();
Cache::forget($model->getSettingsCacheKey());
});
}

/* -----------------------------------------------------------------
| Settings Manager
| -----------------------------------------------------------------
*/
public function settings(): SettingsManagerContract
{
return new TableSettingsManager($this);
return $this->_settingsManager ??= new TableSettingsManager($this);
}

/**
* @return array
/* -----------------------------------------------------------------
| Settings Value (lazy + in-memory)
| -----------------------------------------------------------------
*/
public function getSettingsValue(): array
{
if (config('model_settings.settings_table_use_cache')) {
return Cache::rememberForever($this->getSettingsCacheKey(), function () {
return $this->__getSettingsValue();
});
if ($this->_settingsCache !== null) {
return $this->_settingsCache;
}

if (! config('model_settings.settings_table_use_cache')) {
return $this->_settingsCache = $this->loadSettingsFromDatabase();
}

return $this->__getSettingsValue();
return $this->_settingsCache = Cache::rememberForever(
$this->getSettingsCacheKey(),
fn () => $this->loadSettingsFromDatabase()
);
}

private function __getSettingsValue(): array
/**
* Reload settings and refresh all caches.
*/
public function refreshSettings(): array
{
if ($modelSettings = $this->modelSettings()->first()) {
return $modelSettings->settings;
}
$this->flushSettingsCache();
Cache::forget($this->getSettingsCacheKey());

return [];
return $this->getSettingsValue();
}

/**
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
* Flush only the in-RAM copy.
*/
public function flushSettingsCache(): void
{
$this->_settingsCache = null;
}

/**
* Raw DB hit (no caching layer).
*/
private function loadSettingsFromDatabase(): array
{
/** @var ModelSettings|null $row */
$row = $this->modelSettings()->first(['settings']);

return $row?->settings ?? [];
}

/* -----------------------------------------------------------------
| Relations
| -----------------------------------------------------------------
*/
public function modelSettings(): MorphOne
{
return $this->morphOne(ModelSettings::class, 'model');
}


/* -----------------------------------------------------------------
| Helpers
| -----------------------------------------------------------------
*/
public function getSettingsCacheKey(): string
{
return config('model_settings.settings_table_cache_prefix') . $this->getTable() . '::' . $this->getKey();
return config('model_settings.settings_table_cache_prefix')
. $this->getTable() . '::' . $this->getKey();
}

/* -----------------------------------------------------------------
| Abstract requirements
| -----------------------------------------------------------------
*/
abstract public function getTable();
}
}