This commit is contained in:
Ty Clifford
2026-06-14 15:25:31 -04:00
parent 2f67ae718f
commit b7eaa81501
34 changed files with 3814 additions and 2 deletions
+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Budget;
use PDO;
final class Settings
{
private const DEFAULTS = [
'app.name' => 'Neon Ledger',
'app.currency_symbol' => '$',
'app.timezone' => 'America/New_York',
'theme.default' => 'dark',
'modules.income' => true,
'modules.bills' => true,
'modules.calendar' => true,
'modules.dead_account' => true,
'modules.comparisons' => true,
'modules.alerts' => true,
'modules.import_export' => true,
'dead.income_mode' => 'percent',
'dead.income_value' => 0,
'dead.transaction_mode' => 'fixed',
'dead.transaction_value' => 0,
'dead.hide_balance' => true,
'alerts.email' => '',
'alerts.windows' => [7, 3, 1],
'alerts.times' => ['09:00'],
'mailgun.api_key' => '',
'mailgun.domain' => '',
'mailgun.region' => 'us',
'mailgun.from' => 'Budget Planner <budget@example.com>',
'security.2fa_required' => false,
];
public function __construct(private PDO $pdo)
{
$this->seed();
}
public function get(string $key, mixed $fallback = null): mixed
{
$statement = $this->pdo->prepare('SELECT setting_value FROM settings WHERE setting_key = :key');
$statement->execute(['key' => $key]);
$value = $statement->fetchColumn();
if ($value === false) {
return self::DEFAULTS[$key] ?? $fallback;
}
$decoded = json_decode((string) $value, true);
return json_last_error() === JSON_ERROR_NONE ? $decoded : $value;
}
public function set(string $key, mixed $value): void
{
$encoded = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($encoded === false) {
throw new \InvalidArgumentException('Setting value cannot be encoded.');
}
$sql = $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME) === 'mysql'
? 'INSERT INTO settings (setting_key, setting_value) VALUES (:key, :value)
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), updated_at = CURRENT_TIMESTAMP'
: 'INSERT INTO settings (setting_key, setting_value) VALUES (:key, :value)
ON CONFLICT(setting_key) DO UPDATE SET setting_value = excluded.setting_value, updated_at = CURRENT_TIMESTAMP';
$statement = $this->pdo->prepare($sql);
$statement->execute(['key' => $key, 'value' => $encoded]);
}
/** @return array<string, mixed> */
public function all(): array
{
$settings = self::DEFAULTS;
foreach ($this->pdo->query('SELECT setting_key, setting_value FROM settings')->fetchAll() as $row) {
$decoded = json_decode((string) $row['setting_value'], true);
$settings[$row['setting_key']] = json_last_error() === JSON_ERROR_NONE ? $decoded : $row['setting_value'];
}
return $settings;
}
public function module(string $name): bool
{
return (bool) $this->get('modules.' . $name, true);
}
private function seed(): void
{
$count = (int) $this->pdo->query('SELECT COUNT(*) FROM settings')->fetchColumn();
if ($count > 0) {
return;
}
foreach (self::DEFAULTS as $key => $value) {
$this->set($key, $value);
}
}
}