Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
42.05% covered (danger)
42.05%
37 / 88
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
PathManager
42.05% covered (danger)
42.05%
37 / 88
16.67% covered (danger)
16.67%
1 / 6
168.90
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 path
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 persist
64.29% covered (warning)
64.29%
18 / 28
0.00% covered (danger)
0.00%
0 / 1
9.23
 prepareUrlPath
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 createUrlPaths
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
5.01
 saveUrlPaths
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Node;
6
7use Celema\Quma\Database;
8use Cosray\Exception\RuntimeException;
9use Cosray\Locale;
10use Cosray\Locales;
11use Cosray\Uid;
12
13class PathManager
14{
15    public function __construct(
16        private readonly Uid $uid = new Uid(Uid::ALPHABET_LOWERCASE_WORD_SAFE, 13),
17    ) {}
18
19    public function path(array $rawData, ?Locale $locale, Locale $requestLocale): string
20    {
21        $paths = $rawData['paths'];
22
23        if (!$locale) {
24            $locale = $requestLocale;
25        }
26
27        while ($locale) {
28            if (isset($paths[$locale->id])) {
29                return $paths[$locale->id];
30            }
31
32            $locale = $locale->fallback();
33        }
34
35        throw new RuntimeException('No url path found');
36    }
37
38    public function persist(
39        Database $db,
40        array $data,
41        int $editor,
42        int $nodeId,
43        Locales $locales,
44    ): void {
45        $noPathsGiven = true;
46
47        foreach ($data['paths'] ?? [] as $path) {
48            if (!$path) {
49                continue;
50            }
51
52            $noPathsGiven = false;
53            break;
54        }
55
56        if ($noPathsGiven) {
57            $data['paths'] = $data['generatedPaths'];
58        }
59
60        $defaultLocale = $locales->getDefault();
61        $defaultPath = trim($data['paths'][$defaultLocale->id] ?? '');
62
63        if (!$defaultPath) {
64            throw new RuntimeException(sprintf(
65                'The URL path for the main language "%s" must be set',
66                $defaultLocale->title,
67            ));
68        }
69
70        $currentPaths = array_column($db->nodes->getPaths(['node' => $nodeId])->all(), 'path', 'locale');
71
72        if ($currentPaths) {
73            $baseStructure = [];
74
75            foreach ($locales as $locale) {
76                $baseStructure[$locale->id] = '';
77            }
78
79            $this->saveUrlPaths(
80                $db,
81                array_merge($baseStructure, $currentPaths),
82                $data['paths'],
83                $editor,
84                $nodeId,
85            );
86        } else {
87            $this->createUrlPaths($db, $data['paths'], $editor, $nodeId);
88        }
89    }
90
91    private function prepareUrlPath(Database $db, string $path): string
92    {
93        if (!str_starts_with($path, '/')) {
94            $path = '/' . $path;
95        }
96
97        $db->nodes->deleteInactivePath(['path' => $path])->run();
98
99        return $path;
100    }
101
102    private function createUrlPaths(Database $db, array $paths, int $editor, int $node): void
103    {
104        $alreadyPersisted = [];
105
106        foreach ($paths as $locale => $path) {
107            if (!$path) {
108                continue;
109            }
110
111            $this->prepareUrlPath($db, $path);
112
113            if (in_array($path, $alreadyPersisted, true)) {
114                continue;
115            }
116
117            if ($db->nodes->pathExists(['path' => $path])->first()) {
118                $path = $path . '-' . $this->uid->generate(5);
119            }
120
121            $db->nodes->savePath([
122                'node' => $node,
123                'path' => $path,
124                'locale' => $locale,
125                'editor' => $editor,
126            ])->run();
127
128            $alreadyPersisted[] = $path;
129        }
130    }
131
132    private function saveUrlPaths(
133        Database $db,
134        array $currentPaths,
135        array $paths,
136        int $editor,
137        int $node,
138    ): void {
139        $alreadyPersisted = [];
140
141        foreach ($currentPaths as $locale => $currentPath) {
142            $newPath = trim($paths[$locale] ?? '');
143
144            if ($newPath) {
145                $newPath = $this->prepareUrlPath($db, $newPath);
146
147                if ($currentPath) {
148                    if ($currentPath === $newPath) {
149                        $alreadyPersisted[] = $newPath;
150
151                        continue;
152                    }
153
154                    $db->nodes->deactivatePath([
155                        'path' => $currentPath,
156                        'locale' => $locale,
157                        'editor' => $editor,
158                    ])->run();
159                }
160
161                if (in_array($newPath, $alreadyPersisted, true)) {
162                    continue;
163                }
164
165                if ($db->nodes->pathExists(['path' => $newPath])->first()) {
166                    $newPath = $newPath . '-' . $this->uid->generate(5);
167                }
168
169                $db->nodes->savePath([
170                    'node' => $node,
171                    'path' => $newPath,
172                    'locale' => $locale,
173                    'editor' => $editor,
174                ])->run();
175
176                $alreadyPersisted[] = $newPath;
177            } else {
178                if ($currentPath) {
179                    $db->nodes->deactivatePath([
180                        'path' => $currentPath,
181                        'locale' => $locale,
182                        'editor' => $editor,
183                    ])->run();
184                }
185            }
186        }
187    }
188}