1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace LaravelUi5\OData\Http;
6:
7: /**
8: * OData **custom query options** — the non-`$`, non-`@` parameters on a request
9: * URL (e.g. `?roleCode=customer`).
10: *
11: * Carried as a property on {@see ODataRequest} (populated by the HTTP entry points
12: * from the parsed URL), threaded through the query plan, and handed to an entity
13: * set's `query(CustomQueryOptions $options)` by the resolver. Passing it as data —
14: * rather than reaching for the global Illuminate request — is what makes it correct
15: * under `$batch`: each inner request builds its own `ODataRequest` with its own
16: * options, so there is no shared state and nothing to read off the outer envelope.
17: */
18: final readonly class CustomQueryOptions
19: {
20: /** @param array<string, string> $options */
21: public function __construct(private array $options = []) {}
22:
23: public function get(string $key, ?string $default = null): ?string
24: {
25: return $this->options[$key] ?? $default;
26: }
27:
28: /** @return array<string, string> */
29: public function all(): array
30: {
31: return $this->options;
32: }
33:
34: /**
35: * Build from a parsed query map, keeping only custom query options — string
36: * keys that don't begin with `$` (system options) or `@` (parameter aliases).
37: *
38: * @param array<string, mixed> $query
39: */
40: public static function fromQuery(array $query): self
41: {
42: $custom = [];
43:
44: foreach ($query as $key => $value) {
45: if (is_string($key) && $key !== '' && $key[0] !== '$' && $key[0] !== '@' && is_string($value)) {
46: $custom[$key] = $value;
47: }
48: }
49:
50: return new self($custom);
51: }
52: }
53: