Passed
Push — master ( e6d303...118b88 )
by Caen
02:58 queued 12s
created

AbstractPage::getOutputPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Hyde\Framework\Contracts;
4
5
use Hyde\Framework\Actions\SourceFileParser;
6
use Hyde\Framework\Concerns\FrontMatter\Schemas\PageSchema;
7
use Hyde\Framework\Helpers\Features;
8
use Hyde\Framework\Helpers\Meta;
9
use Hyde\Framework\Hyde;
10
use Hyde\Framework\Models\FrontMatter;
11
use Hyde\Framework\Models\Pages\MarkdownPost;
12
use Hyde\Framework\Models\Route;
13
use Hyde\Framework\Services\DiscoveryService;
14
use Hyde\Framework\Services\RssFeedService;
15
use Illuminate\Support\Collection;
16
17
/**
18
 * To ensure compatibility with the Hyde Framework, all Page Models should extend this class.
19
 *
20
 * Markdown-based Pages can extend the AbstractMarkdownPage class to get relevant helpers.
21
 *
22
 * To learn about what the methods do, see the PHPDocs in the PageContract.
23
 *
24
 * @see \Hyde\Framework\Contracts\PageContract
25
 * @see \Hyde\Framework\Contracts\AbstractMarkdownPage
26
 * @see \Hyde\Framework\Testing\Feature\AbstractPageTest
27
 */
28
abstract class AbstractPage implements PageContract, CompilableContract
29
{
30
    use PageSchema;
31
32
    public static string $sourceDirectory;
33
    public static string $outputDirectory;
34
    public static string $fileExtension;
35
    public static string $template;
36
37
    public string $identifier;
38
    public FrontMatter $matter;
39
40
    /** @inheritDoc */
41
    final public static function getSourceDirectory(): string
42
    {
43
        return unslash(static::$sourceDirectory);
44
    }
45
46
    /** @inheritDoc */
47
    final public static function getOutputDirectory(): string
48
    {
49
        return unslash(static::$outputDirectory);
50
    }
51
52
    /** @inheritDoc */
53
    final public static function getFileExtension(): string
54
    {
55
        return '.'.ltrim(static::$fileExtension, '.');
56
    }
57
58
    /** @inheritDoc */
59
    public static function parse(string $slug): PageContract
60
    {
61
        return (new SourceFileParser(static::class, $slug))->get();
62
    }
63
64
    /** @inheritDoc */
65
    public static function files(): array|false
66
    {
67
        return DiscoveryService::getSourceFileListForModel(static::class);
68
    }
69
70
    /** @inheritDoc */
71
    public static function all(): Collection
72
    {
73
        $collection = new Collection();
74
75
        foreach (static::files() as $basename) {
76
            $collection->push(static::parse($basename));
77
        }
78
79
        return $collection;
80
    }
81
82
    /** @inheritDoc */
83
    public static function qualifyBasename(string $basename): string
84
    {
85
        return static::getSourceDirectory().'/'.unslash($basename).static::getFileExtension();
86
    }
87
88
    /** @inheritDoc */
89
    public static function getOutputLocation(string $basename): string
90
    {
91
        // Using the trim function we ensure we don't have a leading slash when the output directory is the root directory.
92
        return trim(
93
            static::getOutputDirectory().'/'.unslash($basename),
94
            '/'
95
        ).'.html';
96
    }
97
98
    public function __construct(string $identifier = '', FrontMatter|array $matter = [])
99
    {
100
        $this->identifier = $identifier;
101
        $this->matter = $matter instanceof FrontMatter ? $matter : new FrontMatter($matter);
0 ignored issues
show
introduced by
$matter is never a sub-type of Hyde\Framework\Models\FrontMatter.
Loading history...
102
        $this->constructPageSchema();
103
    }
104
105
    /** @inheritDoc */
106
    public function get(string $key = null, mixed $default = null): mixed
107
    {
108
        if (property_exists($this, $key) && isset($this->$key)) {
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type null; however, parameter $property of property_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
        if (property_exists($this, /** @scrutinizer ignore-type */ $key) && isset($this->$key)) {
Loading history...
109
            return $this->$key;
110
        }
111
112
        return $this->matter($key, $default);
113
    }
114
115
    /** @inheritDoc */
116
    public function matter(string $key = null, mixed $default = null): mixed
117
    {
118
        return $this->matter->get($key, $default);
119
    }
120
121
    /** @inheritDoc */
122
    public function getIdentifier(): string
123
    {
124
        return $this->identifier;
125
    }
126
127
    /** @inheritDoc */
128
    public function getSourcePath(): string
129
    {
130
        return static::qualifyBasename($this->identifier);
131
    }
132
133
    /** @inheritDoc */
134
    public function getOutputPath(): string
135
    {
136
        return static::getCurrentPagePath().'.html';
0 ignored issues
show
Bug Best Practice introduced by
The method Hyde\Framework\Contracts...e::getCurrentPagePath() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

136
        return static::/** @scrutinizer ignore-call */ getCurrentPagePath().'.html';
Loading history...
137
    }
138
139
    /** @inheritDoc */
140
    public function getCurrentPagePath(): string
141
    {
142
        return trim(static::getOutputDirectory().'/'.$this->identifier, '/');
143
    }
144
145
    /** @inheritDoc */
146
    public function getRoute(): Route
147
    {
148
        return new Route($this);
149
    }
150
151
    /** @inheritDoc */
152
    public function htmlTitle(): string
153
    {
154
        return config('site.name', 'HydePHP').' - '.$this->title;
155
    }
156
157
    /** @inheritDoc */
158
    public function getBladeView(): string
159
    {
160
        return static::$template;
161
    }
162
163
    /** @inheritDoc */
164
    abstract public function compile(): string;
165
166
    public function getCanonicalUrl(): string
167
    {
168
        return $this->getRoute()->getQualifiedUrl();
169
    }
170
171
    /**
172
     * @return string[]
173
     *
174
     * @psalm-return list<string>
175
     */
176
    public function getDynamicMetadata(): array
177
    {
178
        $array = [];
179
180
        if ($this->canUseCanonicalUrl()) {
181
            $array[] = '<link rel="canonical" href="'.$this->getCanonicalUrl().'" />';
182
        }
183
184
        if (Features::sitemap()) {
185
            $array[] = '<link rel="sitemap" type="application/xml" title="Sitemap" href="'.Hyde::url('sitemap.xml').'" />';
186
        }
187
188
        if (Features::rss()) {
189
            $array[] = $this->makeRssFeedLink();
190
        }
191
192
        if (isset($this->title)) {
193
            if ($this->hasTwitterTitleInConfig()) {
194
                $array[] = '<meta name="twitter:title" content="'.$this->htmlTitle().'" />';
195
            }
196
            if ($this->hasOpenGraphTitleInConfig()) {
197
                $array[] = '<meta property="og:title" content="'.$this->htmlTitle().'" />';
198
            }
199
        }
200
201
        if ($this instanceof MarkdownPost) {
202
            $array[] = "\n<!-- Blog Post Meta Tags -->";
203
            foreach ($this->getMetadata() as $name => $content) {
204
                $array[] = Meta::name($name, $content);
205
            }
206
            foreach ($this->getMetaProperties() as $property => $content) {
207
                $array[] = Meta::property($property, $content);
208
            }
209
        }
210
211
        return $array;
212
    }
213
214
    public function renderPageMetadata(): string
215
    {
216
        return Meta::render(
217
            withMergedData: $this->getDynamicMetadata()
218
        );
219
    }
220
221
    public function canUseCanonicalUrl(): bool
222
    {
223
        return Hyde::hasSiteUrl() && isset($this->identifier);
224
    }
225
226
    public function hasTwitterTitleInConfig(): bool
227
    {
228
        return str_contains(json_encode(config('hyde.meta', [])), 'twitter:title');
229
    }
230
231
    public function hasOpenGraphTitleInConfig(): bool
232
    {
233
        return str_contains(json_encode(config('hyde.meta', [])), 'og:title');
234
    }
235
236
    protected function makeRssFeedLink(): string
237
    {
238
        return '<link rel="alternate" type="application/rss+xml" title="'.RssFeedService::getDescription().
239
            '" href="'.Hyde::url(RssFeedService::getDefaultOutputFilename()).'" />';
240
    }
241
242
    public function showInNavigation(): bool
243
    {
244
        return ! $this->navigation['hidden'];
245
    }
246
247
    public function navigationMenuPriority(): int
248
    {
249
        return $this->navigation['priority'];
250
    }
251
252
    public function navigationMenuTitle(): string
253
    {
254
        return $this->navigation['title'];
255
    }
256
257
    /**
258
     * Not yet implemented.
259
     *
260
     * If an item returns a route collection,
261
     * it will automatically be made into a dropdown.
262
     *
263
     * @return \Illuminate\Support\Collection<\Hyde\Framework\Models\Route>
264
     */
265
    // public function navigationMenuChildren(): Collection;
266
}
267