Passed
Push — master ( 00b783...b7fb13 )
by Caen
03:02 queued 11s
created

AbstractPage::all()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
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 getSourcePath(): string
123
    {
124
        return static::qualifyBasename($this->identifier);
125
    }
126
127
    /** @inheritDoc */
128
    public function getOutputPath(): string
129
    {
130
        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

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