-
-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathQuery.php
More file actions
287 lines (252 loc) · 8.33 KB
/
Query.php
File metadata and controls
287 lines (252 loc) · 8.33 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
<?php
declare(strict_types=1);
namespace Kreait\Firebase\Database;
use Kreait\Firebase\Database\Query\Filter;
use Kreait\Firebase\Database\Query\Filter\EndAt;
use Kreait\Firebase\Database\Query\Filter\EndBefore;
use Kreait\Firebase\Database\Query\Filter\EqualTo;
use Kreait\Firebase\Database\Query\Filter\LimitToFirst;
use Kreait\Firebase\Database\Query\Filter\LimitToLast;
use Kreait\Firebase\Database\Query\Filter\Shallow;
use Kreait\Firebase\Database\Query\Filter\StartAfter;
use Kreait\Firebase\Database\Query\Filter\StartAt;
use Kreait\Firebase\Database\Query\Sorter;
use Kreait\Firebase\Database\Query\Sorter\OrderByChild;
use Kreait\Firebase\Database\Query\Sorter\OrderByKey;
use Kreait\Firebase\Database\Query\Sorter\OrderByValue;
use Kreait\Firebase\Exception\Database\DatabaseNotFound;
use Kreait\Firebase\Exception\Database\UnsupportedQuery;
use Kreait\Firebase\Exception\DatabaseException;
use Psr\Http\Message\UriInterface;
use Stringable;
/**
* A Query sorts and filters the data at a database location so only a subset of the child data is included.
* This can be used to order a collection of data by some attribute (e.g. height of dinosaurs) as well as
* to restrict a large list of items (e.g. chat messages) down to a number suitable for synchronizing
* to the client. Queries are created by chaining together one or more of the filter methods
* defined here.
*
* Just as with a Reference, you can receive data from a Query by using the
* {@see getSnapshot()} or {@see getValue()} method. You will only receive
* Snapshots for the subset of the data that matches your query.
*/
class Query implements Stringable
{
/**
* @var Filter[]
*/
private array $filters = [];
private ?Sorter $sorter = null;
/**
* @internal
*/
public function __construct(private readonly Reference $reference, private readonly ApiClient $apiClient)
{
}
/**
* Returns the absolute URL for this location.
*
* @see getUri()
*/
public function __toString(): string
{
return (string) $this->getUri();
}
/**
* Returns a Reference to the Query's location.
*/
public function getReference(): Reference
{
return $this->reference;
}
/**
* Returns a data snapshot of the current location.
*
* @throws UnsupportedQuery if an error occurred
*/
public function getSnapshot(): Snapshot
{
$uri = $this->getUri();
$pathAndQuery = $uri->getPath().'?'.$uri->getQuery();
try {
$value = $this->apiClient->get($pathAndQuery);
} catch (DatabaseNotFound $e) {
throw $e;
} catch (DatabaseException $e) {
throw new UnsupportedQuery(
query: $this,
message: $e->getMessage(),
previous: $e->getPrevious()
);
}
if ($this->sorter !== null) {
$value = $this->sorter->modifyValue($value);
}
foreach ($this->filters as $filter) {
$value = $filter->modifyValue($value);
}
return new Snapshot($this->reference, $value);
}
/**
* Convenience method for {@see getSnapshot()}->getValue().
*
* @throws UnsupportedQuery if an error occurred
*/
public function getValue(): mixed
{
return $this->getSnapshot()->getValue();
}
/**
* Creates a Query with the specified ending point.
*
* The ending point is inclusive, so children with exactly
* the specified value will be included in the query.
*
* @param scalar $value
*/
public function endAt($value): self
{
return $this->withAddedFilter(new EndAt($value));
}
/**
* Creates a Query with the specified ending point (exclusive).
*
* @param scalar $value
*/
public function endBefore($value): self
{
return $this->withAddedFilter(new EndBefore($value));
}
/**
* Creates a Query which includes children which match the specified value.
*
* @param scalar $value
*/
public function equalTo($value): self
{
return $this->withAddedFilter(new EqualTo($value));
}
/**
* Creates a Query with the specified starting point (inclusive).
*
* @param scalar $value
*/
public function startAt($value): self
{
return $this->withAddedFilter(new StartAt($value));
}
/**
* Creates a Query with the specified starting point (exclusive).
*
* @param scalar $value
*/
public function startAfter($value): self
{
return $this->withAddedFilter(new StartAfter($value));
}
/**
* Generates a new Query limited to the first specific number of children.
*/
public function limitToFirst(int $limit): self
{
return $this->withAddedFilter(new LimitToFirst($limit));
}
/**
* Generates a new Query object limited to the last specific number of children.
*/
public function limitToLast(int $limit): self
{
return $this->withAddedFilter(new LimitToLast($limit));
}
/**
* Generates a new Query object ordered by the specified child key.
*
* Queries can only order by one key at a time. Calling orderBy*() multiple times on
* the same query is an error.
*
* @throws UnsupportedQuery if the query is already ordered
*/
public function orderByChild(string $childKey): self
{
return $this->withSorter(new OrderByChild($childKey));
}
/**
* Generates a new Query object ordered by key.
*
* Sorts the results of a query by their ascending key value.
*
* Queries can only order by one key at a time. Calling orderBy*() multiple times on
* the same query is an error.
*
* @throws UnsupportedQuery if the query is already ordered
*/
public function orderByKey(): self
{
return $this->withSorter(new OrderByKey());
}
/**
* Generates a new Query object ordered by child values.
*
* If the children of a query are all scalar values (numbers or strings), you can order the results
* by their (ascending) values.
*
* Queries can only order by one key at a time. Calling orderBy*() multiple times on
* the same query is an error.
*
* @throws UnsupportedQuery if the query is already ordered
*/
public function orderByValue(): self
{
return $this->withSorter(new OrderByValue());
}
/**
* This is an advanced feature, designed to help you work with large datasets without needing to download
* everything. Set this to true to limit the depth of the data returned at a location. If the data at
* the location is a JSON primitive (string, number or boolean), its value will simply be returned.
*
* If the data snapshot at the location is a JSON object, the values for each key will be
* truncated to true.
*
* @see https://firebase.google.com/docs/reference/rest/database/#section-param-shallow
*/
public function shallow(): self
{
return $this->withAddedFilter(new Shallow());
}
/**
* 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
{
$uri = $this->reference->getUri();
if ($this->sorter !== null) {
$uri = $this->sorter->modifyUri($uri);
}
foreach ($this->filters as $filter) {
$uri = $filter->modifyUri($uri);
}
return $uri;
}
private function withAddedFilter(Filter $filter): self
{
$query = clone $this;
$query->filters[] = $filter;
return $query;
}
private function withSorter(Sorter $sorter): self
{
if ($this->sorter !== null) {
throw new UnsupportedQuery($this, 'This query is already ordered.');
}
$query = clone $this;
$query->sorter = $sorter;
return $query;
}
}