Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
15 / 15
CRAP
100.00% covered (success)
100.00%
1 / 1
CollectionUrls
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
15 / 15
19
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
 path
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 collection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 back
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 expand
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 collapse
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 children
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 showInTree
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 edit
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 paths
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 bulk
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createPaths
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 url
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Panel;
6
7final class CollectionUrls
8{
9    public function __construct(
10        private readonly string $panelPath,
11        private readonly string $slug,
12        public readonly CollectionQuery $query,
13    ) {}
14
15    public function path(): string
16    {
17        return $this->panelPath . '/collection/' . rawurlencode($this->slug);
18    }
19
20    /** @param array<string, mixed> $overrides */
21    public function collection(array $overrides = []): string
22    {
23        return $this->url($this->path(), $this->query->listParams($overrides));
24    }
25
26    /** @param array<string, mixed> $overrides */
27    public function back(array $overrides = []): string
28    {
29        return $this->url($this->path(), $this->query->editorParams($overrides));
30    }
31
32    public function expand(string $uid): string
33    {
34        $open = $this->query->open;
35
36        if (!in_array($uid, $open, true)) {
37            $open[] = $uid;
38        }
39
40        return $this->collection([
41            'view' => 'tree',
42            'open' => $open,
43        ]);
44    }
45
46    /** @param list<string> $descendants */
47    public function collapse(string $uid, array $descendants = []): string
48    {
49        $closed = array_fill_keys(array_merge([$uid], $descendants), true);
50        $open = array_values(array_filter(
51            $this->query->open,
52            static fn(string $openUid): bool => !isset($closed[$openUid]),
53        ));
54
55        return $this->collection([
56            'view' => 'tree',
57            'open' => $open,
58        ]);
59    }
60
61    public function children(string $uid): string
62    {
63        return $this->collection([
64            'parent' => $uid,
65            'view' => '',
66            'open' => '',
67            'offset' => '',
68        ]);
69    }
70
71    public function showInTree(string $uid): string
72    {
73        $open = $this->query->open;
74
75        if (!in_array($uid, $open, true)) {
76            $open[] = $uid;
77        }
78
79        return $this->collection([
80            'parent' => '',
81            'view' => 'tree',
82            'open' => $open,
83            'offset' => '',
84        ]);
85    }
86
87    public function edit(string $uid): string
88    {
89        $path = $this->path() . '/' . rawurlencode($uid);
90
91        return $this->url($path, $this->query->editorParams());
92    }
93
94    /**
95     * Editor sub-routes carry no query â€” appending to edit() would land
96     * the suffix inside its query string.
97     */
98    public function paths(string $uid): string
99    {
100        return $this->path() . '/' . rawurlencode($uid) . '/paths';
101    }
102
103    public function delete(string $uid): string
104    {
105        return $this->path() . '/' . rawurlencode($uid) . '/delete';
106    }
107
108    /**
109     * Bulk-action endpoint, carrying the full listing query (offset
110     * included) so the response can redirect back to the same view.
111     */
112    public function bulk(string $action): string
113    {
114        $path = $this->path() . '/bulk/' . rawurlencode($action);
115
116        return $this->url($path, $this->query->editorParams());
117    }
118
119    public function createPaths(string $type): string
120    {
121        return $this->path() . '/create/' . rawurlencode($type) . '/paths';
122    }
123
124    public function create(string $type, ?string $parent = null): string
125    {
126        $overrides = [];
127
128        if ($parent !== null) {
129            $overrides['parent'] = $parent;
130        }
131
132        $path = $this->path() . '/create/' . rawurlencode($type);
133
134        return $this->url($path, $this->query->editorParams($overrides));
135    }
136
137    /** @param array<string, mixed> $params */
138    private function url(string $path, array $params): string
139    {
140        $query = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
141
142        return $query === '' ? $path : $path . '?' . $query;
143    }
144}