-
-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathReference.php
More file actions
393 lines (348 loc) · 10.8 KB
/
Reference.php
File metadata and controls
393 lines (348 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
<?php
declare(strict_types=1);
namespace Kreait\Firebase\Database;
use Kreait\Firebase\Exception\DatabaseException;
use Kreait\Firebase\Exception\InvalidArgumentException;
use Kreait\Firebase\Exception\OutOfRangeException;
use Psr\Http\Message\UriInterface;
use Stringable;
use function array_fill_keys;
use function array_keys;
use function array_map;
use function basename;
use function dirname;
use function is_array;
use function ltrim;
use function sprintf;
use function trim;
/**
* A Reference represents a specific location in your database and can be used
* for reading or writing data to that database location.
*/
class Reference implements Stringable
{
/**
* @internal
*/
public function __construct(
private readonly UriInterface $uri,
private readonly ApiClient $apiClient,
) {
}
/**
* Returns the absolute URL for this location.
*
* @see getUri()
*/
public function __toString(): string
{
return (string) $this->getUri();
}
/**
* The last part of the current path.
*
* For example, "ada" is the key for https://sample-app.firebaseio.example.com/users/ada.
*
* The key of the root Reference is null.
*/
public function getKey(): ?string
{
$key = basename($this->getPath());
return $key !== '' ? $key : null;
}
/**
* Returns the full path to a reference.
*/
public function getPath(): string
{
return trim($this->uri->getPath(), '/');
}
/**
* The parent location of a Reference.
*
* @throws OutOfRangeException if requested for the root Reference
*/
public function getParent(): self
{
$parentPath = dirname($this->getPath());
if ($parentPath === '.') {
throw new OutOfRangeException('Cannot get parent of root reference');
}
return new self($this->uri->withPath('/'.ltrim($parentPath, '/')), $this->apiClient);
}
/**
* The root location of a Reference.
*/
public function getRoot(): self
{
return new self($this->uri->withPath('/'), $this->apiClient);
}
/**
* Gets a Reference for the location at the specified relative path.
*
* The relative path can either be a simple child name (for example, "ada")
* or a deeper slash-separated path (for example, "ada/name/first").
*
* @throws InvalidArgumentException if the path is invalid
*/
public function getChild(string $path): self
{
$childPath = sprintf('/%s/%s', trim($this->uri->getPath(), '/'), trim($path, '/'));
try {
return new self($this->uri->withPath($childPath), $this->apiClient);
} catch (\InvalidArgumentException $e) {
throw new InvalidArgumentException(message: $e->getMessage(), previous: $e);
}
}
/**
* Generates a new Query object ordered by the specified child key.
*
* @see Query::orderByChild()
*/
public function orderByChild(string $path): Query
{
return $this->query()->orderByChild($path);
}
/**
* Generates a new Query object ordered by key.
*
* @see Query::orderByKey()
*/
public function orderByKey(): Query
{
return $this->query()->orderByKey();
}
/**
* Generates a new Query object ordered by child values.
*
* @see Query::orderByValue()
*/
public function orderByValue(): Query
{
return $this->query()->orderByValue();
}
/**
* Generates a new Query limited to the first specific number of children.
*
* @see Query::limitToFirst()
*/
public function limitToFirst(int $limit): Query
{
return $this->query()->limitToFirst($limit);
}
/**
* Generates a new Query object limited to the last specific number of children.
*
* @see Query::limitToLast()
*/
public function limitToLast(int $limit): Query
{
return $this->query()->limitToLast($limit);
}
/**
* Creates a Query with the specified starting point (inclusive).
*
* @see Query::startAt()
*/
public function startAt(bool|string|int|float $value): Query
{
return $this->query()->startAt($value);
}
/**
* Creates a Query with the specified starting point (exclusive).
*
* @see Query::startAfter()
*/
public function startAfter(bool|string|int|float $value): Query
{
return $this->query()->startAfter($value);
}
/**
* Creates a Query with the specified ending point (inclusive).
*
* @see Query::endAt()
*/
public function endAt(bool|string|int|float $value): Query
{
return $this->query()->endAt($value);
}
/**
* Creates a Query with the specified ending point (exclusive).
*
* @see Query::endBefore()
*/
public function endBefore(bool|string|int|float $value): Query
{
return $this->query()->endBefore($value);
}
/**
* Creates a Query which includes children which match the specified value.
*
* @see Query::equalTo()
*/
public function equalTo(bool|string|int|float $value): Query
{
return $this->query()->equalTo($value);
}
/**
* Creates a Query with shallow results.
*
* @see Query::shallow()
*/
public function shallow(): Query
{
return $this->query()->shallow();
}
/**
* Returns the keys of a reference's children.
*
* @throws DatabaseException if the API reported an error
* @throws OutOfRangeException if the reference has no children with keys
*
* @return string[]
*/
public function getChildKeys(): array
{
$snapshot = $this->shallow()->getSnapshot();
if (is_array($value = $snapshot->getValue())) {
return array_map(strval(...), array_keys($value));
}
throw new OutOfRangeException(sprintf('%s has no children with keys', $this));
}
/**
* Convenience method for {@see getSnapshot()}->getValue().
*
* @throws DatabaseException if the API reported an error
*/
public function getValue(): mixed
{
return $this->getSnapshot()->getValue();
}
/**
* Write data to this database location.
*
* This will overwrite any data at this location and all child locations.
*
* Passing null for the new value is equivalent to calling {@see remove()}:
* all data at this location or any child location will be deleted.
*
* @throws DatabaseException if the API reported an error
*/
public function set(mixed $value): self
{
if ($value === null) {
$this->apiClient->remove($this->uri->getPath());
} else {
$this->apiClient->set($this->uri->getPath(), $value);
}
return $this;
}
/**
* Returns a data snapshot of the current location.
*
* @throws DatabaseException if the API reported an error
*/
public function getSnapshot(): Snapshot
{
$value = $this->apiClient->get($this->uri->getPath());
return new Snapshot($this, $value);
}
/**
* Generates a new child location using a unique key and returns its reference.
*
* This is the most common pattern for adding data to a collection of items.
*
* If you provide a value to push(), the value will be written to the generated location.
* If you don't pass a value, nothing will be written to the database and the child
* will remain empty (but you can use the reference elsewhere).
*
* The unique key generated by push() are ordered by the current time, so the resulting
* list of items will be chronologically sorted. The keys are also designed to be
* unguessable (they contain 72 random bits of entropy).
*
* @param mixed|null $value
*
* @throws DatabaseException if the API reported an error
*/
public function push($value = null): self
{
$value ??= [];
$newKey = $this->apiClient->push($this->uri->getPath(), $value);
$newPath = sprintf('%s/%s', $this->uri->getPath(), $newKey);
return new self($this->uri->withPath($newPath), $this->apiClient);
}
/**
* Remove the data at this database location.
*
* Any data at child locations will also be deleted.
*
* @throws DatabaseException if the API reported an error
*/
public function remove(): self
{
$this->apiClient->remove($this->uri->getPath());
return $this;
}
/**
* Remove the data at the given locations.
*
* Each location can either be a simple property (for example, "name"), or a relative path
* (for example, "name/first") from the current location to the data to remove.
*
* Any data at child locations will also be deleted.
*
* @param string[] $keys Locations to remove
*
* @throws DatabaseException
*/
public function removeChildren(array $keys): self
{
$this->update(
array_fill_keys($keys, null),
);
return $this;
}
/**
* Writes multiple values to the database at once.
*
* The values argument contains multiple property/value pairs that will be written to the database together.
* Each child property can either be a simple property (for example, "name"), or a relative path
* (for example, "name/first") from the current location to the data to update.
*
* As opposed to the {@see set()} method, update() can be use to selectively update only the referenced properties
* at the current location (instead of replacing all the child properties at the current location).
*
* Passing null to {see update()} will remove the data at this location.
*
* @param array<mixed> $values
*
* @throws DatabaseException if the API reported an error
*/
public function update(array $values): self
{
$this->apiClient->update($this->uri->getPath(), $values);
return $this;
}
/**
* Returns the absolute URL for this location.
*
* This method returns a URL that is ready to be put into a browser, curl command, or a
* {@see Database::getReferenceFromUrl()} call. Since all of those expect the URL
* to be url-encoded, toString() returns an encoded URL.
*
* Append '.json' to the URL when typed into a browser to download JSON formatted data.
* If the location is secured (not publicly readable),
* you will get a permission-denied error.
*/
public function getUri(): UriInterface
{
return $this->uri;
}
/**
* Returns a new query for the current reference.
*/
private function query(): Query
{
return new Query($this, $this->apiClient);
}
}