226 lines
6.5 KiB
PHP
226 lines
6.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* ============================================================
|
|
*
|
|
* @project biiproject
|
|
*
|
|
* @author Andika Debi Putra
|
|
*
|
|
* @email andikadebiputra@gmail.com
|
|
*
|
|
* @website https://biiproject.com
|
|
*
|
|
* @copyright Copyright (c) 2026 Andika Debi Putra
|
|
* @license Proprietary - All Rights Reserved
|
|
*
|
|
* @version 1.0.0
|
|
*
|
|
* @created 2026-05-01
|
|
* ============================================================
|
|
*
|
|
* Unauthorized copying, modification, distribution, or use
|
|
* of this file is strictly prohibited without prior written
|
|
* permission from the author.
|
|
* ============================================================
|
|
*/
|
|
|
|
namespace App\Services\SystemConfig;
|
|
|
|
use App\Models\SystemSettingRevision;
|
|
use App\Repositories\SystemSettingRepository;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
class SystemConfigService
|
|
{
|
|
private const CACHE_KEY = 'system_settings.all';
|
|
|
|
private const PUBLIC_CACHE_KEY = 'system_settings.public';
|
|
|
|
private const CACHE_TTL_MINUTES = 60;
|
|
|
|
protected static ?array $resolvedSettings = null;
|
|
|
|
public function __construct(
|
|
protected SystemSettingRepository $repository,
|
|
protected SettingFileUploader $fileUploader = new SettingFileUploader,
|
|
) {}
|
|
|
|
public static function definitions(): array
|
|
{
|
|
return SettingDefinitions::ALL;
|
|
}
|
|
|
|
public function all(bool $forceRefresh = false): array
|
|
{
|
|
if ($forceRefresh) {
|
|
$this->invalidateCache();
|
|
}
|
|
|
|
if (self::$resolvedSettings !== null) {
|
|
return self::$resolvedSettings;
|
|
}
|
|
|
|
try {
|
|
return self::$resolvedSettings = Cache::remember(
|
|
self::CACHE_KEY,
|
|
now()->addMinutes(self::CACHE_TTL_MINUTES),
|
|
fn () => $this->resolveAll(),
|
|
);
|
|
} catch (\Exception) {
|
|
return self::$resolvedSettings = $this->resolveAll();
|
|
}
|
|
}
|
|
|
|
public function getPublicSettings(): array
|
|
{
|
|
try {
|
|
return Cache::remember(
|
|
self::PUBLIC_CACHE_KEY,
|
|
now()->addMinutes(self::CACHE_TTL_MINUTES),
|
|
fn () => $this->buildPublicSettings(),
|
|
);
|
|
} catch (\Exception) {
|
|
return $this->buildPublicSettings();
|
|
}
|
|
}
|
|
|
|
public function grouped(): array
|
|
{
|
|
$all = $this->all();
|
|
$grouped = [];
|
|
|
|
foreach (SettingDefinitions::ALL as $key => $meta) {
|
|
$grouped[$meta['group']][$key] = $all[$key] ?? $meta['default'] ?? null;
|
|
}
|
|
|
|
return $grouped;
|
|
}
|
|
|
|
public function get(string $key, mixed $default = null): mixed
|
|
{
|
|
if (! array_key_exists($key, SettingDefinitions::ALL)) {
|
|
return $default;
|
|
}
|
|
|
|
return $this->all()[$key] ?? $default;
|
|
}
|
|
|
|
public function update(array $input, array $files = [], ?int $actorId = null, ?Request $request = null): void
|
|
{
|
|
$current = $this->all();
|
|
|
|
foreach (SettingDefinitions::ALL as $key => $meta) {
|
|
$oldValue = $current[$key] ?? $meta['default'] ?? null;
|
|
$newValue = $this->resolveNewValue($key, $meta, $input, $files, $oldValue);
|
|
|
|
if (SettingValueCaster::isUnchanged($oldValue, $newValue)) {
|
|
continue;
|
|
}
|
|
|
|
$setting = $this->repository->upsert([
|
|
'key' => $key,
|
|
'value' => SettingValueCaster::serialize($newValue),
|
|
'type' => $meta['type'],
|
|
'group' => $meta['group'],
|
|
'is_public' => (bool) $meta['is_public'],
|
|
'description' => $meta['description'],
|
|
'created_by' => $actorId,
|
|
'updated_by' => $actorId,
|
|
]);
|
|
|
|
$this->writeRevision($setting->id, $key, $oldValue, $newValue, $actorId, $request);
|
|
}
|
|
|
|
$this->invalidateCache();
|
|
}
|
|
|
|
protected function resolveNewValue(string $key, array $meta, array $input, array $files, mixed $oldValue): mixed
|
|
{
|
|
if ($meta['type'] === 'image_path') {
|
|
return isset($files[$key])
|
|
? $this->fileUploader->replace($key, $files[$key], $oldValue)
|
|
: $oldValue;
|
|
}
|
|
|
|
return array_key_exists($key, $input)
|
|
? SettingValueCaster::normalize($input[$key], (string) $meta['type'])
|
|
: $oldValue;
|
|
}
|
|
|
|
protected function resolveAll(): array
|
|
{
|
|
try {
|
|
if (! $this->repository->tableExists()) {
|
|
return $this->definitionDefaults();
|
|
}
|
|
$rows = $this->repository->all();
|
|
} catch (\Exception) {
|
|
return $this->definitionDefaults();
|
|
}
|
|
|
|
$resolved = [];
|
|
foreach (SettingDefinitions::ALL as $key => $meta) {
|
|
$record = $rows->firstWhere('key', $key);
|
|
$resolved[$key] = $record
|
|
? SettingValueCaster::deserialize($record->value, (string) $record->type)
|
|
: ($meta['default'] ?? null);
|
|
}
|
|
|
|
return $resolved;
|
|
}
|
|
|
|
protected function buildPublicSettings(): array
|
|
{
|
|
$all = $this->all();
|
|
$public = [];
|
|
|
|
foreach (SettingDefinitions::ALL as $key => $meta) {
|
|
if (! $meta['is_public']) {
|
|
continue;
|
|
}
|
|
$public[$key] = $all[$key] ?? $meta['default'] ?? null;
|
|
}
|
|
|
|
return $public;
|
|
}
|
|
|
|
protected function definitionDefaults(): array
|
|
{
|
|
$defaults = [];
|
|
foreach (SettingDefinitions::ALL as $key => $meta) {
|
|
$defaults[$key] = $meta['default'] ?? null;
|
|
}
|
|
|
|
return $defaults;
|
|
}
|
|
|
|
protected function writeRevision(
|
|
int $settingId,
|
|
string $key,
|
|
mixed $oldValue,
|
|
mixed $newValue,
|
|
?int $actorId,
|
|
?Request $request,
|
|
): void {
|
|
SystemSettingRevision::query()->create([
|
|
'system_setting_id' => $settingId,
|
|
'key' => $key,
|
|
'old_value' => $oldValue === null ? null : json_encode($oldValue, JSON_UNESCAPED_SLASHES),
|
|
'new_value' => $newValue === null ? null : json_encode($newValue, JSON_UNESCAPED_SLASHES),
|
|
'changed_by' => $actorId,
|
|
'changed_ip' => $request?->ip(),
|
|
'changed_agent' => $request?->userAgent(),
|
|
'created_at' => now(),
|
|
]);
|
|
}
|
|
|
|
protected function invalidateCache(): void
|
|
{
|
|
Cache::forget(self::CACHE_KEY);
|
|
Cache::forget(self::PUBLIC_CACHE_KEY);
|
|
self::$resolvedSettings = null;
|
|
}
|
|
}
|