Passed
Push — master ( 1a998a...943704 )
by Caen
07:42 queued 04:24
created

NavigationDataFactory   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 27
eloc 69
c 8
b 0
f 0
dl 0
loc 160
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A toArray() 0 7 1
A __construct() 0 12 1
A makeLabel() 0 6 1
A searchForLabelInConfig() 0 12 2
A findPriorityInSidebarConfig() 0 11 2
A isInstanceOf() 0 3 1
A findPriorityInNavigationConfig() 0 3 2
A makeGroup() 0 13 4
A makePriority() 0 9 3
A makeHidden() 0 19 6
A getDocumentationPageGroup() 0 8 2
A getSubdirectoryConfiguration() 0 3 1
A findGroupFromMatter() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Framework\Factories;
6
7
use function array_flip;
8
use function array_key_exists;
9
use function array_merge;
10
use function config;
11
use Hyde\Framework\Concerns\InteractsWithFrontMatter;
12
use Hyde\Framework\Factories\Concerns\CoreDataObject;
13
use Hyde\Markdown\Contracts\FrontMatter\SubSchemas\NavigationSchema;
14
use Hyde\Markdown\Models\FrontMatter;
15
use Hyde\Pages\DocumentationPage;
16
use Hyde\Pages\MarkdownPost;
17
use Illuminate\Support\Str;
18
use function in_array;
19
use function is_a;
20
21
/**
22
 * Discover data used for navigation menus and the documentation sidebar.
23
 */
24
class NavigationDataFactory extends Concerns\PageDataFactory implements NavigationSchema
25
{
26
    use InteractsWithFrontMatter;
27
28
    /**
29
     * The front matter properties supported by this factory.
30
     *
31
     * Note that this represents a sub-schema, and is used as part of the page schema.
32
     */
33
    public const SCHEMA = NavigationSchema::NAVIGATION_SCHEMA;
34
35
    protected const FALLBACK_PRIORITY = 999;
36
    protected const CONFIG_OFFSET = 500;
37
38
    protected readonly ?string $label;
39
    protected readonly ?string $group;
40
    protected readonly ?bool $hidden;
41
    protected readonly ?int $priority;
42
    private readonly string $title;
43
    private readonly string $routeKey;
44
    private readonly string $pageClass;
45
    private readonly string $identifier;
46
    private readonly FrontMatter $matter;
47
48
    public function __construct(CoreDataObject $pageData, string $title)
49
    {
50
        $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...
51
        $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...
52
        $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...
53
        $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...
54
        $this->title = $title;
0 ignored issues
show
Bug introduced by
The property title is declared read-only in Hyde\Framework\Factories\NavigationDataFactory.
Loading history...
55
56
        $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...
57
        $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...
58
        $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...
59
        $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...
60
    }
61
62
    public function toArray(): array
63
    {
64
        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...
65
            'label' => $this->label,
66
            'group' => $this->group,
67
            'hidden' => $this->hidden,
68
            'priority' => $this->priority,
69
        ];
70
    }
71
72
    protected function makeLabel(): ?string
73
    {
74
        return $this->matter('navigation.label')
75
            ?? $this->searchForLabelInConfig()
76
            ?? $this->matter('title')
77
            ?? $this->title;
78
    }
79
80
    protected function makeGroup(): ?string
81
    {
82
        if ($this->isInstanceOf(DocumentationPage::class)) {
83
            return $this->getDocumentationPageGroup();
84
        }
85
86
        if (Str::contains($this->identifier, '/') && $this->getSubdirectoryConfiguration() === 'dropdown') {
87
            return Str::before($this->identifier, '/');
88
        }
89
90
        // TODO Check in front matter for group?
91
92
        return null;
93
    }
94
95
    protected function makeHidden(): ?bool
96
    {
97
        if ($this->isInstanceOf(MarkdownPost::class)) {
98
            return true;
99
        }
100
101
        if ($this->matter('navigation.hidden', false)) {
102
            return true;
103
        }
104
105
        if (in_array($this->routeKey, config('hyde.navigation.exclude', ['404']))) {
106
            return true;
107
        }
108
109
        if (Str::contains($this->identifier, '/') && $this->getSubdirectoryConfiguration() === 'hidden') {
110
            return true;
111
        }
112
113
        return false;
114
    }
115
116
    protected function makePriority(): ?int
117
    {
118
        if ($this->matter('navigation.priority') !== null) {
119
            return $this->matter('navigation.priority');
120
        }
121
122
        return $this->isInstanceOf(DocumentationPage::class)
123
            ? $this->findPriorityInSidebarConfig(array_flip(config('docs.sidebar_order', []))) ?? self::FALLBACK_PRIORITY
124
            : $this->findPriorityInNavigationConfig(config('hyde.navigation.order', [])) ?? self::FALLBACK_PRIORITY;
125
    }
126
127
    private function findPriorityInNavigationConfig(array $config): ?int
128
    {
129
        return array_key_exists($this->routeKey, $config) ? (int) $config[$this->routeKey] : null;
130
    }
131
132
    private function findPriorityInSidebarConfig(array $config): ?int
133
    {
134
        // Sidebars uses a special syntax where the keys are just the page identifiers in a flat array
135
136
        // Adding 250 makes so that pages with a front matter priority that is lower can be shown first.
137
        // It's lower than the fallback of 500 so that the config ones still come first.
138
        // This is all to make it easier to mix ways of adding priorities.
139
140
        return isset($config[$this->identifier])
141
            ? $config[$this->identifier] + (self::CONFIG_OFFSET)
142
            : null;
143
    }
144
145
    private function getDocumentationPageGroup(): ?string
146
    {
147
        // If the documentation page is in a subdirectory,
148
        return str_contains($this->identifier, '/')
149
            // then we can use that as the category name.
150
            ? Str::before($this->identifier, '/')
151
            // Otherwise, we look in the front matter.
152
            : $this->findGroupFromMatter();
153
    }
154
155
    protected function searchForLabelInConfig(): ?string
156
    {
157
        $labelConfig = array_merge([
158
            'index' => 'Home',
159
            'docs/index' => 'Docs',
160
        ], config('hyde.navigation.labels', []));
161
162
        if (isset($labelConfig[$this->routeKey])) {
163
            return $labelConfig[$this->routeKey];
164
        }
165
166
        return null;
167
    }
168
169
    protected function isInstanceOf(string $class): bool
170
    {
171
        return is_a($this->pageClass, $class, true);
172
    }
173
174
    protected function findGroupFromMatter(): mixed
175
    {
176
        return $this->matter('navigation.group')
177
            ?? $this->matter('navigation.category')
178
            ?? 'other';
179
    }
180
181
    protected static function getSubdirectoryConfiguration(): string
182
    {
183
        return config('hyde.navigation.subdirectories', 'hidden');
184
    }
185
}
186