diff options
| author | winter | 2025-01-26 15:25:17 +0000 |
|---|---|---|
| committer | winter | 2025-01-26 15:25:17 +0000 |
| commit | 890dad24d6c095c41e6358b2a6cb61f1b6c2f6ad (patch) | |
| tree | a662dfd61585b755054075cd93121a9c70a48983 /Digitigrade | |
| parent | 5841d6550936e1fc34f1fbdf4325b1e27ff79230 (diff) | |
add user settings / preferences
Diffstat (limited to 'Digitigrade')
| -rw-r--r-- | Digitigrade/GlobalSettings.php | 4 | ||||
| -rw-r--r-- | Digitigrade/Model.php | 16 | ||||
| -rw-r--r-- | Digitigrade/UserSettings.php | 169 |
3 files changed, 185 insertions, 4 deletions
diff --git a/Digitigrade/GlobalSettings.php b/Digitigrade/GlobalSettings.php index 3e893e1..e8bbe59 100644 --- a/Digitigrade/GlobalSettings.php +++ b/Digitigrade/GlobalSettings.php @@ -52,6 +52,10 @@ class GlobalSettings extends Singleton { $stmt->execute([$key, $value]); } + public function setBool(string $key, bool $value) { + $this->set($key, $value ? 'true' : 'false'); + } + /** * Checks whether a setting is set. * @param string $key which setting to check diff --git a/Digitigrade/Model.php b/Digitigrade/Model.php index 6a4fe27..9ce759a 100644 --- a/Digitigrade/Model.php +++ b/Digitigrade/Model.php @@ -6,7 +6,7 @@ use Digitigrade\Model\FetchableModel; abstract class Model { private bool $saved = false; - protected static array $cachedLookups = []; + private static array $cachedLookups = []; private static bool $memoEnabled = false; protected function setOwnerId(int $id) { @@ -164,7 +164,7 @@ abstract class Model { $result = static::fromDbRow($stmt->fetch(\PDO::FETCH_ASSOC)); if (self::$memoEnabled) { - self::$cachedLookups[$memoKey] = $result; + self::$cachedLookups[$memoKey] = &$result; } return $result; } @@ -201,7 +201,7 @@ abstract class Model { return static::fromDbRow($row); }, $stmt->fetchAll(\PDO::FETCH_ASSOC)); if (self::$memoEnabled) { - self::$cachedLookups[$memoKey] = $result; + self::$cachedLookups[$memoKey] = &$result; } return $result; } @@ -229,7 +229,7 @@ abstract class Model { $result = $stmt->fetchColumn(0); if (self::$memoEnabled) { - self::$cachedLookups[$memoKey] = $result; + self::$cachedLookups[$memoKey] = &$result; } return $result; } @@ -326,6 +326,14 @@ abstract class Model { $tableName = self::mangleName($className); $db->exec("DELETE FROM $tableName WHERE $where"); + + if (self::$memoEnabled) { + $key = array_search($this, self::$cachedLookups, strict: true); + while ($key !== false) { + unset(self::$cachedLookups[$key]); + $key = array_search($this, self::$cachedLookups, strict: true); + } + } } /** diff --git a/Digitigrade/UserSettings.php b/Digitigrade/UserSettings.php new file mode 100644 index 0000000..4a27014 --- /dev/null +++ b/Digitigrade/UserSettings.php @@ -0,0 +1,169 @@ +<?php +namespace Digitigrade; + +use Digitigrade\Model\UserAccount; + +class UserSettings { + private const DEFINITIONS_INI_PATH = __DIR__ . '/../settings.user.ini'; + + private \PDO $db; + private array $info; + private UserAccount $user; + + public function __construct(UserAccount $user) { + $this->user = $user; + $this->db = Db::getInstance()->getPdo(); + if (!$this->get('settings.initialised')) { + $this->resetAll(); + } + } + + /** + * Gets a setting's value. + * @param string $key which setting to get + * @return ?string the value, or null if not set + */ + public function get(string $key): ?string { + $stmt = $this->db->prepare('SELECT value FROM user_setting WHERE name = ? AND user_id = ?'); + $stmt->execute([$key, $this->user->id]); + return $stmt->fetchColumn(0) ?: null; + } + + /** + * Gets a boolean setting's value. + * @param string $key which setting to get + * @return ?bool the value, or null if not set + */ + public function getBool(string $key): ?bool { + return match ($this->get($key)) { + '', 'false' => false, + '1', 'true' => true, + default => null + }; + } + + /** + * Sets the value of a setting. + * @param string $key which setting to modify + * @param string $value the new value + * @return void + */ + public function set(string $key, string $value) { + $stmt = $this->db->prepare(<<<END + INSERT INTO user_setting(user_id, name, value) VALUES (?, ?, ?) + ON CONFLICT (user_id, name) DO UPDATE SET value = EXCLUDED.value + END); + $stmt->execute([$this->user->id, $key, $value]); + } + + public function setBool(string $key, bool $value) { + $this->set($key, $value ? 'true' : 'false'); + } + + /** + * Checks whether a setting is set. + * @param string $key which setting to check + * @return bool if it's set (has a value) or not + */ + public function isset(string $key): bool { + $stmt = $this->db->prepare('SELECT count(*) FROM user_setting WHERE name = ? AND user_id = ?'); + $stmt->execute([$key, $this->user->id]); + return $stmt->fetchColumn(0) > 0; + } + + /** + * Unsets a setting. + * @param string $key which setting to remove + * @return void + */ + public function unset(string $key) { + $stmt = $this->db->prepare('DELETE FROM user_setting WHERE name = ? AND user_id = ?'); + $stmt->execute([$key, $this->user->id]); + } + + /** + * Gets a list of all currently-set settings. + * @return array{key: string, value: string} array of keys to values + */ + public function getAll(): array { + $stmt = $this->db->prepare('SELECT name, value FROM user_setting WHERE user_id = ?'); + $stmt->execute([$this->user->id]); + $items = $stmt->fetchAll(); + $result = []; + foreach ($items as $row) { + $result[$row['name']] = $row['value']; + } + return $result; + } + + /** + * Gets a list of all settings in a given prefix. + * @param string $prefix prefix to look in (not including the trailing dot) + * @return array{key: string, value: string} array of keys to values + */ + public function getAllInPrefix(string $prefix): array { + $stmt = $this->db->prepare('SELECT name, value FROM user_setting WHERE name ^@ ? AND user_id = ?'); + $stmt->execute(["$prefix.", $this->user->id]); + $items = $stmt->fetchAll(); + $result = []; + foreach ($items as $row) { + $result[$row['name']] = $row['value']; + } + return $result; + } + + /** + * Gets a list of all first-level prefixes. + * @return string[] + */ + public function getAllPrefixes(): array { + return array_unique(array_map( + fn($x) => explode('.', $x, 2)[0], + array_keys($this->getAll()) + )); + } + + /** + * Gets information (name, description, default value) for a given setting. + * @param string $key which setting to look up + * @return ?array{item: string, value: string} associative array of info, + * or null if none is available + */ + public function getInfo(string $key): ?array { + $this->loadDefinitions(); + return $this->info[$key] ?? null; + } + + /** + * Merges new keys from the settings definition file into the settings + * without overwriting existing values. + * @return void + */ + public function mergeUpdates() { + $this->initialise(false); + } + + /** + * Sets all defined settings back to their default values. + * @return void + */ + public function resetAll() { + $this->initialise(true); + } + + private function loadDefinitions() { + if (!isset($this->info)) { + $this->info = parse_ini_file(self::DEFINITIONS_INI_PATH, process_sections: true); + } + } + + private function initialise(bool $overwriteExisting) { + $this->loadDefinitions(); + foreach ($this->info as $key => $setting) { + if ($overwriteExisting || !$this->isset($key)) { + $this->set($key, $setting['default']); + } + } + $this->set('settings.initialised', true); + } +}
\ No newline at end of file |
