Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
MediaScreen
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
6 / 6
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromParams
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
9
 withFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 filtered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 url
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 query
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Panel;
6
7use Cosray\Assets\Library;
8
9/**
10 * The media screen's state as its URL carries it: filter kinds, search,
11 * upload date range, selected file and listing page. Every control of the
12 * screen is a link or a GET form built from it, so a reload or a shared
13 * link shows the same screen.
14 */
15final readonly class MediaScreen
16{
17    /** @param list<string> $kinds */
18    public function __construct(
19        public string $path,
20        public array $kinds = [],
21        public string $q = '',
22        public string $range = '',
23        public ?string $file = null,
24        public int $page = 1,
25    ) {}
26
27    /** @param array<array-key, mixed> $params */
28    public static function fromParams(string $path, array $params): self
29    {
30        $range = is_string($params['range'] ?? null) ? $params['range'] : '';
31        $file = is_string($params['file'] ?? null) ? trim($params['file']) : '';
32
33        $kinds = $params['kind'] ?? '';
34
35        return new self(
36            path: $path,
37            // The URL carries a comma list, the rail's checkboxes an array.
38            kinds: Library::filterKinds(match (true) {
39                is_string($kinds) => $kinds,
40                is_array($kinds) => array_values(array_filter($kinds, is_string(...))),
41                default => '',
42            }),
43            q: is_string($params['q'] ?? null) ? trim($params['q']) : '',
44            range: in_array($range, Library::RANGES, true) ? $range : '',
45            file: $file !== '' ? $file : null,
46            page: max(1, (int) ($params['page'] ?? 1)),
47        );
48    }
49
50    public function withFile(?string $file): self
51    {
52        return new self($this->path, $this->kinds, $this->q, $this->range, $file, $this->page);
53    }
54
55    public function filtered(): bool
56    {
57        return $this->kinds !== [] || $this->q !== '' || $this->range !== '';
58    }
59
60    /**
61     * The screen's URL with some parts changed: `kind` takes a list,
62     * `file` null to deselect, the rest strings; `page` falls back to the
63     * first unless given. `$below` addresses a path under the screen's, such
64     * as a file's save action, with the state still in the query.
65     *
66     * @param array{kind?: list<string>, q?: string, range?: string, file?: ?string, page?: int} $changes
67     */
68    public function url(array $changes = [], string $below = ''): string
69    {
70        $path = $below === '' ? $this->path : "{$this->path}/{$below}";
71        $query = http_build_query($this->query($changes), encoding_type: PHP_QUERY_RFC3986);
72
73        return $query === '' ? $path : "{$path}?{$query}";
74    }
75
76    /**
77     * The non-empty query parameters, as hidden inputs of a GET form need them.
78     *
79     * @param array{kind?: list<string>, q?: string, range?: string, file?: ?string, page?: int} $changes
80     * @return array<string, string>
81     */
82    public function query(array $changes = []): array
83    {
84        $params = [
85            'kind' => implode(',', $changes['kind'] ?? $this->kinds),
86            'q' => $changes['q'] ?? $this->q,
87            'range' => $changes['range'] ?? $this->range,
88            'file' => array_key_exists('file', $changes) ? (string) $changes['file'] : (string) $this->file,
89            'page' => (string) ($changes['page'] ?? 1),
90        ];
91
92        if ($params['page'] === '1') {
93            $params['page'] = '';
94        }
95
96        return array_filter($params, static fn(string $value): bool => $value !== '');
97    }
98}