Passed
Push — master ( 916e40...59dd7b )
by Caen
03:21 queued 14s
created

searchForPriorityInSidebarConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 0
Metric Value
cc 1
eloc 3
c 6
b 1
f 0
nc 1
nop 0
dl 0
loc 11
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Framework\Factories;
6
7
use function array_flip;
8
use function config;
9
use Hyde\Framework\Concerns\InteractsWithFrontMatter;
10
use Hyde\Framework\Factories\Concerns\CoreDataObject;
11
use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\NavigationSchema;
12
use Hyde\Markdown\Models\FrontMatter;
13
use Hyde\Pages\DocumentationPage;
14
use Hyde\Pages\MarkdownPost;
15
use Illuminate\Support\Arr;
16
use Illuminate\Support\Str;
17
use function in_array;
18
use function is_a;
19
20
/**
21
 * Discover data used for navigation menus and the documentation sidebar.
22
 */
23
class NavigationDataFactory extends Concerns\PageDataFactory implements NavigationSchema
24
{
25
    use InteractsWithFrontMatter;
26
27
    /**
28
     * The front matter properties supported by this factory.
29
     *
30
     * Note that this represents a sub-schema, and is used as part of the page schema.
31
     */
32
    public const SCHEMA = NavigationSchema::NAVIGATION_SCHEMA;
33
34
    protected const FALLBACK_PRIORITY = 999;
35
    protected const CONFIG_OFFSET = 500;
36
37
    protected readonly ?string $label;
38
    protected readonly ?string $group;
39
    protected readonly ?bool $hidden;
40
    protected readonly ?int $priority;
41
    private readonly string $title;
42
    private readonly string $routeKey;
43
    private readonly string $pageClass;
44
    private readonly string $identifier;
45
    private readonly FrontMatter $matter;
46
47
    public function __construct(CoreDataObject $pageData, string $title)
48
    {
49
        $this->matter = $pageData->matter;
0 ignored issues
show
Bug introduced by
The property matter is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
50
        $this->identifier = $pageData->identifier;
0 ignored issues
show
Bug introduced by
The property identifier is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
51
        $this->pageClass = $pageData->pageClass;
0 ignored issues
show
Bug introduced by
The property pageClass is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
52
        $this->routeKey = $pageData->routeKey;
0 ignored issues
show
Bug introduced by
The property routeKey is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
53
        $this->title = $title;
0 ignored issues
show
Bug introduced by
The property title is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
54
55
        $this->label = $this->makeLabel();
0 ignored issues
show
Bug introduced by
The property label is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
56
        $this->group = $this->makeGroup();
0 ignored issues
show
Bug introduced by
The property group is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
57
        $this->hidden = $this->makeHidden();
0 ignored issues
show
Bug introduced by
The property hidden is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
58
        $this->priority = $this->makePriority();
0 ignored issues
show
Bug introduced by
The property priority is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
59
    }
60
61
    public function toArray(): array
62
    {
63
        return [
0 ignored issues
show
introduced by
The expression return array('label' => ...ty' => $this->priority) returns an array which contains values of type boolean|integer|string which are incompatible with the return type Illuminate\Contracts\Support\TValue mandated by Illuminate\Contracts\Support\Arrayable::toArray().
Loading history...
64
            'label' => $this->label,
65
            'group' => $this->group,
66
            'hidden' => $this->hidden,
67
            'priority' => $this->priority,
68
        ];
69
    }
70
71
    protected function makeLabel(): ?string
72
    {
73
        return $this->searchForLabelInFrontMatter()
74
            ?? $this->searchForLabelInConfig()
75
            ?? $this->matter('title')
76
            ?? $this->title;
77
    }
78
79
    protected function makeGroup(): ?string
80
    {
81
        if ($this->pageIsInSubdirectory() && $this->canUseSubdirectoryForGroups()) {
82
            return $this->getSubdirectoryName();
83
        }
84
85
        return $this->searchForGroupInFrontMatter() ?? $this->defaultGroup();
86
    }
87
88
    protected function makeHidden(): ?bool
89
    {
90
        return $this->isInstanceOf(MarkdownPost::class)
91
            || $this->searchForHiddenInFrontMatter()
92
            || in_array($this->routeKey, config('hyde.navigation.exclude', ['404']))
93
            || $this->pageIsInSubdirectory() && ($this->getSubdirectoryConfiguration() === 'hidden');
94
    }
95
96
    protected function makePriority(): int
97
    {
98
        return $this->searchForPriorityInFrontMatter()
99
            ?? $this->searchForPriorityInConfigs()
100
            ?? self::FALLBACK_PRIORITY;
101
    }
102
103
    private function searchForLabelInFrontMatter(): ?string
104
    {
105
        return $this->matter('navigation.label')
106
            ?? $this->matter('navigation.title');
107
    }
108
109
    private function searchForGroupInFrontMatter(): ?string
110
    {
111
        return $this->matter('navigation.group')
112
            ?? $this->matter('navigation.category');
113
    }
114
115
    private function searchForHiddenInFrontMatter(): ?bool
116
    {
117
        return $this->matter('navigation.hidden')
118
            ?? $this->invert($this->matter('navigation.visible'));
119
    }
120
121
    private function searchForPriorityInFrontMatter(): ?int
122
    {
123
        return $this->matter('navigation.priority')
124
            ?? $this->matter('navigation.order');
125
    }
126
127
    private function searchForLabelInConfig(): ?string
128
    {
129
        return Arr::get(config('hyde.navigation.labels', [
130
            'index' => 'Home',
131
            'docs/index' => 'Docs',
132
        ]), $this->routeKey);
133
    }
134
135
    private function searchForPriorityInConfigs(): ?int
136
    {
137
        return $this->isInstanceOf(DocumentationPage::class)
138
            ? $this->searchForPriorityInSidebarConfig()
139
            : $this->searchForPriorityInNavigationConfig();
140
    }
141
142
    private function searchForPriorityInSidebarConfig(): ?int
143
    {
144
        // Sidebars uses a special syntax where the keys are just the page identifiers in a flat array.
145
        // TODO: In the future we could normalize this with the standard navigation config so both strategies can be auto-detected and used.
146
147
        // Adding an offset makes so that pages with a front matter priority that is lower can be shown first.
148
        // This is all to make it easier to mix ways of adding priorities.
149
150
        return $this->offset(Arr::get(
151
            array_flip(config('docs.sidebar_order', [])), $this->identifier),
152
            self::CONFIG_OFFSET
153
        );
154
    }
155
156
    private function searchForPriorityInNavigationConfig(): ?int
157
    {
158
        return config("hyde.navigation.order.$this->routeKey");
159
    }
160
161
    private function canUseSubdirectoryForGroups(): bool
162
    {
163
        return $this->getSubdirectoryConfiguration() === 'dropdown'
164
            || $this->isInstanceOf(DocumentationPage::class);
165
    }
166
167
    private function defaultGroup(): ?string
168
    {
169
        return $this->isInstanceOf(DocumentationPage::class) ? 'other' : null;
170
    }
171
172
    private function pageIsInSubdirectory(): bool
173
    {
174
        return Str::contains($this->identifier, '/');
175
    }
176
177
    private function getSubdirectoryName(): string
178
    {
179
        return Str::before($this->identifier, '/');
180
    }
181
182
    protected function getSubdirectoryConfiguration(): string
183
    {
184
        return config('hyde.navigation.subdirectories', 'hidden');
185
    }
186
187
    protected function isInstanceOf(string $class): bool
188
    {
189
        return is_a($this->pageClass, $class, true);
190
    }
191
192
    protected function invert(?bool $value): ?bool
193
    {
194
        return $value === null ? null : ! $value;
195
    }
196
197
    protected function offset(?int $value, int $offset): ?int
198
    {
199
        return $value === null ? null : $value + $offset;
200
    }
201
}
202