*/ protected function casts(): array { return ['value' => 'json']; } public static function getValue(string $key, mixed $default = null): mixed { return Cache::rememberForever( "app-setting.{$key}", fn (): mixed => static::query()->find($key)?->value, ) ?? $default; } public static function getBool(string $key, bool $default = false): bool { return (bool) static::getValue($key, $default); } /** * Liest direkt aus der Datenbank, am Cache vorbei — für operative * Fortschrittsmarken (Wasserstand der Aufruf-Aggregation, Audit N21): * Ein veralteter Cachewert würde dort jeden Lauf gegen die Zeilensperre * scheitern lassen, ohne sich je zu korrigieren. */ public static function getValueFresh(string $key, mixed $default = null): mixed { $value = static::query()->find($key)?->value; return $value ?? $default; } /** * Cache erst NACH dem Commit verwerfen (Audit N21): Innerhalb einer * Transaktion sähe ein Leser zwischen Forget und Commit noch den alten * DB-Wert und schriebe ihn dauerhaft zurück in den Cache. Ohne offene * Transaktion läuft der Callback sofort. */ public static function setValue(string $key, mixed $value): void { static::query()->updateOrCreate(['key' => $key], ['value' => $value]); DB::afterCommit(static fn () => static::forgetCached($key)); } /** * Verwirft den zwischengespeicherten Wert. Nötig überall dort, wo an * `app_settings` vorbei geschrieben wird — insbesondere in Migrationen, * die den Query Builder benutzen: `getValue()` cacht dauerhaft * (`rememberForever`), ein Direktschreiber bliebe sonst unsichtbar. */ public static function forgetCached(string $key): void { Cache::forget("app-setting.{$key}"); } }