61 lines
2.1 KiB
PHP
61 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Runtime settings an operator can change from the console.
|
|
*
|
|
* Cached because the public-site gate reads one of these on every single
|
|
* request; the cache is dropped on write, so a flip takes effect immediately
|
|
* rather than "within a minute".
|
|
*/
|
|
final class Settings
|
|
{
|
|
private const CACHE_PREFIX = 'app_setting:';
|
|
|
|
public static function get(string $key, mixed $default = null): mixed
|
|
{
|
|
$raw = Cache::rememberForever(self::CACHE_PREFIX.$key, function () use ($key) {
|
|
try {
|
|
$row = DB::table('app_settings')->where('key', $key)->value('value');
|
|
} catch (QueryException $e) {
|
|
// The gate reads a setting on every request, so an unreachable
|
|
// or not-yet-migrated table would take the whole site down —
|
|
// including during a deploy where new code runs before migrate.
|
|
// Fall back to the caller's default instead, and say so.
|
|
Log::warning('app_settings unavailable, using default', [
|
|
'key' => $key, 'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return '__missing__';
|
|
}
|
|
|
|
// Sentinel: distinguishes "stored null" from "never stored", so a
|
|
// missing row does not get re-queried on every request.
|
|
return $row === null ? '__missing__' : $row;
|
|
});
|
|
|
|
return $raw === '__missing__' ? $default : json_decode($raw, true);
|
|
}
|
|
|
|
public static function set(string $key, mixed $value): void
|
|
{
|
|
DB::table('app_settings')->updateOrInsert(
|
|
['key' => $key],
|
|
['value' => json_encode($value), 'updated_at' => now(), 'created_at' => now()],
|
|
);
|
|
|
|
Cache::forget(self::CACHE_PREFIX.$key);
|
|
}
|
|
|
|
public static function bool(string $key, bool $default = false): bool
|
|
{
|
|
return (bool) self::get($key, $default);
|
|
}
|
|
}
|