Skip to content

Commit 223ac2a

Browse files
committed
Refactor hydration to be context-specific
1 parent fdcffc3 commit 223ac2a

2 files changed

Lines changed: 95 additions & 60 deletions

File tree

src/Handlers/DatabaseHandler.php

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,64 @@
77

88
/**
99
* Provides database storage for Settings.
10+
* Uses local storage to minimize database calls.
1011
*/
1112
class DatabaseHandler extends BaseHandler
1213
{
1314
/**
14-
* Stores our cached settings retrieved
15-
* from the database on the first get() call
16-
* to reduce the number of database calls
17-
* at the expense of a little bit of memory.
15+
* The database table to use.
1816
*
19-
* @var array
17+
* @var string
2018
*/
21-
private $settings = [];
19+
private $table;
2220

2321
/**
24-
* Have the settings been read and cached
25-
* from the database yet?
22+
* Storage for cached general settings.
23+
* Format: ['class' => ['property' => ['value', 'type']]]
2624
*
27-
* @var bool
25+
* @var array<string,array<string,array>>|null Will be null until hydrated
2826
*/
29-
private $hydrated = false;
27+
private $general;
3028

3129
/**
32-
* The settings table
30+
* Storage for cached context settings.
31+
* Format: ['context' => ['class' => ['property' => ['value', 'type']]]]
3332
*
34-
* @var string
33+
* @var array<string,array|null>
3534
*/
36-
private $table;
35+
private $contexts = [];
36+
37+
/**
38+
* Stores the configured database table.
39+
*/
40+
public function __construct()
41+
{
42+
$this->table = config('Settings')->database['table'] ?? 'settings';
43+
}
3744

3845
/**
3946
* Checks whether this handler has a value set.
4047
*/
4148
public function has(string $class, string $property, ?string $context = null): bool
4249
{
43-
$this->hydrate();
50+
$this->hydrate($context);
4451

45-
if (! isset($this->settings[$class][$property])) {
46-
return false;
52+
if ($context === null) {
53+
return isset($this->general[$class])
54+
? array_key_exists($property, $this->general[$class])
55+
: false;
4756
}
4857

49-
return array_key_exists($context ?? 0, $this->settings[$class][$property]);
58+
return isset($this->contexts[$context][$class])
59+
? array_key_exists($property, $this->contexts[$context][$class])
60+
: false;
5061
}
5162

5263
/**
5364
* Attempt to retrieve a value from the database.
5465
* To boost performance, all of the values are
55-
* read and stored in $this->settings the first
56-
* time, and then used from there the rest of the request.
66+
* read and stored the first call for each contexts
67+
* and then retrieved from storage.
5768
*
5869
* @return mixed|null
5970
*/
@@ -63,25 +74,28 @@ public function get(string $class, string $property, ?string $context = null)
6374
return null;
6475
}
6576

66-
return $this->parseValue(...$this->settings[$class][$property][$context ?? 0]);
77+
return $context === null
78+
? $this->parseValue(...$this->general[$class][$property])
79+
: $this->parseValue(...$this->contexts[$context][$class][$property]);
6780
}
6881

6982
/**
7083
* Stores values into the database for later retrieval.
7184
*
7285
* @param mixed $value
7386
*
87+
* @throws RuntimeException For database failures
88+
*
7489
* @return mixed|void
7590
*/
7691
public function set(string $class, string $property, $value = null, ?string $context = null)
7792
{
78-
$this->hydrate();
7993
$time = Time::now()->format('Y-m-d H:i:s');
8094
$type = gettype($value);
8195
$value = $this->prepareValue($value);
8296

83-
// If we found it in our cache, then we need to update
84-
if (isset($this->settings[$class][$property][$context ?? 0])) {
97+
// If it was stored then we need to update
98+
if ($this->has($class, $property, $context)) {
8599
$result = db_connect()->table($this->table)
86100
->where('class', $class)
87101
->where('key', $property)
@@ -91,6 +105,7 @@ public function set(string $class, string $property, $value = null, ?string $con
91105
'context' => $context,
92106
'updated_at' => $time,
93107
]);
108+
// ...otherwise insert it
94109
} else {
95110
$result = db_connect()->table($this->table)
96111
->insert([
@@ -104,19 +119,21 @@ public function set(string $class, string $property, $value = null, ?string $con
104119
]);
105120
}
106121

107-
// Update our cache
122+
// Update storage
108123
if ($result === true) {
109-
if (! array_key_exists($class, $this->settings)) {
110-
$this->settings[$class] = [];
124+
if ($context === null) {
125+
$this->general[$class][$property] = [
126+
$value,
127+
$type,
128+
];
129+
} else {
130+
$this->contexts[$context][$class][$property] = [
131+
$value,
132+
$type,
133+
];
111134
}
112-
if (! array_key_exists($property, $this->settings[$class])) {
113-
$this->settings[$class][$property] = [];
114-
}
115-
116-
$this->settings[$class][$property][$context ?? 0] = [
117-
$value,
118-
$type,
119-
];
135+
} else {
136+
throw new RuntimeException(db_connect()->error()['message'] ?? 'Error writing to the database.');
120137
}
121138

122139
return $result;
@@ -128,9 +145,9 @@ public function set(string $class, string $property, $value = null, ?string $con
128145
*/
129146
public function forget(string $class, string $property, ?string $context = null)
130147
{
131-
$this->hydrate();
148+
$this->hydrate($context);
132149

133-
// Delete from persistent storage
150+
// Delete from the database
134151
$result = db_connect()->table($this->table)
135152
->where('class', $class)
136153
->where('key', $property)
@@ -142,46 +159,64 @@ public function forget(string $class, string $property, ?string $context = null)
142159
}
143160

144161
// Delete from local storage
145-
unset($this->settings[$class][$property][$context ?? 0]);
162+
if ($context === null) {
163+
unset($this->general[$class][$property]);
164+
} else {
165+
unset($this->contexts[$context][$class][$property]);
166+
}
146167

147168
return $result;
148169
}
149170

150171
/**
151-
* Ensures we've pulled all of the values from the database.
172+
* Fetches values from the database in bulk to minimize calls.
173+
* General is always fetched once, contexts are fetched in their
174+
* entirety for each new request.
152175
*
153-
* @throws RuntimeException
176+
* @throws RuntimeException For database failures
154177
*/
155-
private function hydrate()
178+
private function hydrate(?string $context)
156179
{
157-
if ($this->hydrated) {
158-
return;
159-
}
180+
if ($context === null) {
181+
// Check for completion
182+
if ($this->general !== null) {
183+
return;
184+
}
160185

161-
$this->table = config('Settings')->database['table'] ?? 'settings';
186+
$this->general = [];
187+
$query = db_connect()->table($this->table)->where('context', null);
188+
} else {
189+
// Check for completion
190+
if (isset($this->contexts[$context])) {
191+
return;
192+
}
162193

163-
$rawValues = db_connect()->table($this->table)->get();
194+
$query = db_connect()->table($this->table)->where('context', $context);
164195

165-
if (is_bool($rawValues)) {
166-
throw new RuntimeException(db_connect()->error()['message'] ?? 'Error reading from database.');
167-
}
196+
// If general has not been hydrated we will do that at the same time
197+
if ($this->general === null) {
198+
$this->general = [];
199+
$query->orWhere('context', null);
200+
}
168201

169-
$rawValues = $rawValues->getResultObject();
202+
$this->contexts[$context] = [];
203+
}
170204

171-
foreach ($rawValues as $row) {
172-
if (! array_key_exists($row->class, $this->settings)) {
173-
$this->settings[$row->class] = [];
174-
}
175-
if (! array_key_exists($row->key, $this->settings[$row->class])) {
176-
$this->settings[$row->class][$row->key] = [];
177-
}
205+
if (is_bool($result = $query->get())) {
206+
throw new RuntimeException(db_connect()->error()['message'] ?? 'Error reading from database.');
207+
}
178208

179-
$this->settings[$row->class][$row->key][$row->context ?? 0] = [
209+
foreach ($result->getResultObject() as $row) {
210+
$tuple = [
180211
$row->value,
181212
$row->type,
182213
];
183-
}
184214

185-
$this->hydrated = true;
215+
if ($row->context === null) {
216+
$this->general[$row->class][$row->key] = $tuple;
217+
} else {
218+
$this->contexts[$row->context][$row->class][$row->key] = $tuple;
219+
}
220+
}
186221
}
187222
}

src/Settings.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function get(string $key, ?string $context = null)
6969
}
7070
}
7171

72-
// If no contextual value was found then fall back on a global
72+
// If no contextual value was found then fall back to general
7373
if ($context !== null) {
7474
return $this->get($key);
7575
}

0 commit comments

Comments
 (0)