Skip to content

Commit 25dd9da

Browse files
authored
Merge pull request #1782 from hydephp/overhaul-the-authors-feature
[2.x] Overhaul the blog post authors feature
2 parents ebe55fb + 8f1134d commit 25dd9da

File tree

14 files changed

+509
-33
lines changed

14 files changed

+509
-33
lines changed

RELEASE_NOTES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This serves two purposes:
1919
- **Breaking:** The internals of the navigation system has been rewritten into a new Navigation API. This change is breaking for custom navigation implementations. For more information, see below.
2020
- **Breaking:** The `hyde.features` configuration format has changed to use Enums instead of static method calls. For more information, see below.
2121
- **Breaking:** Renamed class `DataCollections` to `DataCollection`. For more information, see below.
22+
- **Breaking:** The `hyde.authors` config setting should now be keyed by the usernames. For more information, see below.
2223
- Medium: The `route` function will now throw a `RouteNotFoundException` if the route does not exist in https://github.com/hydephp/develop/pull/1741
2324
- Minor: Navigation menu items are now no longer filtered by duplicates (meaning two items with the same label can now exist in the same menu) in https://github.com/hydephp/develop/pull/1573
2425
- Minor: Due to changes in the navigation system, it is possible that existing configuration files will need to be adjusted in order for menus to look the same (in terms of ordering etc.)
@@ -29,6 +30,7 @@ This serves two purposes:
2930
- Minor: `Includes::path()` and `Includes::get()` methods now normalizes paths to be basenames to match the behaviour of the other include methods in https://github.com/hydephp/develop/pull/1738. This means that nested directories are no longer supported, as you should use a data collection for that.
3031
- Minor: The `processing_time_ms` attribute in the `sitemap.xml` file has now been removed in https://github.com/hydephp/develop/pull/1744
3132
- Minor: Updated the `Hyde::url()` helper to return `null` instead of throwing a `BaseUrlNotSetException` when no site URL is set and no path was provided to the method in https://github.com/hydephp/develop/pull/1760
33+
- Overhauled the blog post author feature in https://github.com/hydephp/develop/pull/1782
3234
- Improved the sitemap data generation to be smarter and more dynamic in https://github.com/hydephp/develop/pull/1744
3335
- Skipped build tasks will now exit with an exit code of 3 instead of 0 in https://github.com/hydephp/develop/pull/1749
3436
- The `hasFeature` method on the Hyde facade and HydeKernel now only accepts a Feature enum value instead of a string for its parameter.
@@ -47,6 +49,7 @@ This serves two purposes:
4749
- Breaking: Removed the deprecated `\Hyde\Framework\Services\BuildService::transferMediaAssets()` method (see upgrade guide below)
4850
- Removed the deprecated global`unslash()` function, replaced with the namespaced `\Hyde\unslash()` function in https://github.com/hydephp/develop/pull/1754
4951
- Removed the deprecated `BaseUrlNotSetException` class, with the `Hyde::url()` helper now returning `null` if no base URL is set in https://github.com/hydephp/develop/pull/1760
52+
- Removed: The deprecated `PostAuthor::getName()` method is now removed (use `$author->name`) in https://github.com/hydephp/develop/pull/1782
5053
- Internal: Removed the internal `DocumentationSearchPage::generate()` method as it was unused in https://github.com/hydephp/develop/pull/1569
5154

5255
### Fixed
@@ -146,6 +149,23 @@ Of course, if you have disabled any of the features, do not include them in the
146149

147150
## General impact
148151

152+
### Post Author changes
153+
154+
This release makes major improvements into the usability and design of the blog post author feature.
155+
156+
Here is the full list of changes:
157+
158+
- Breaking: The `hyde.authors` config setting should now be keyed by the usernames
159+
- Removed: The deprecated `PostAuthor::getName()` method is now removed (use `$author->name`)
160+
- Feature: We now support setting authors in the YAML configuration! Fixes `#1719`
161+
- Feature: Added a `$author->getPosts()` method to get all author's posts
162+
- Feature: Authors now can also have custom biographies and social media links
163+
- The PostAuthor class is now Arrayable and JsonSerializable
164+
- The collection of site authors are now stored in the HydeKernel
165+
- Authors can additionally be accessed through `Hyde::authors()`
166+
167+
For more information, see https://github.com/hydephp/develop/pull/1782
168+
149169
### Documentation search page changes
150170

151171
The documentation search page and search index have been changed to be generated as `InMemoryPages` instead of a post-build task.

config/hyde.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@
284284
*/
285285

286286
'authors' => [
287-
Author::create(
287+
'mr_hyde' => Author::create(
288288
'mr_hyde', // Required username
289289
'Mr. Hyde', // Optional display name
290290
'https://hydephp.com' // Optional website URL

docs/creating-content/blog-posts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ author:
156156
```
157157

158158
When specifying an array you don't need all the sub-properties. The example just shows all the supported values.
159-
Array values here will override all the values in the `authors` config entry.
159+
Array values here will override all the values in the `authors` config entry, if one exists.
160160

161161
### Image
162162

packages/framework/config/hyde.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@
284284
*/
285285

286286
'authors' => [
287-
Author::create(
287+
'mr_hyde' => Author::create(
288288
'mr_hyde', // Required username
289289
'Mr. Hyde', // Optional display name
290290
'https://hydephp.com' // Optional website URL
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\Foundation\Concerns;
6+
7+
use Hyde\Facades\Config;
8+
use Illuminate\Support\Collection;
9+
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
10+
11+
use function collect;
12+
13+
/**
14+
* Contains accessors and containers for data stored in the kernel.
15+
*
16+
* @internal Single-use trait for the HydeKernel class.
17+
*
18+
* @see \Hyde\Foundation\HydeKernel
19+
*/
20+
trait HasKernelData
21+
{
22+
/**
23+
* The collection of authors defined in the config.
24+
*
25+
* @var \Illuminate\Support\Collection<string, \Hyde\Framework\Features\Blogging\Models\PostAuthor>
26+
*/
27+
protected Collection $authors;
28+
29+
/**
30+
* Get the collection of authors defined in the config.
31+
*
32+
* @return \Illuminate\Support\Collection<string, \Hyde\Framework\Features\Blogging\Models\PostAuthor>
33+
*/
34+
public function authors(): Collection
35+
{
36+
if (isset($this->authors)) {
37+
return $this->authors;
38+
}
39+
40+
$config = collect(Config::getArray('hyde.authors', []));
41+
42+
if ($config->isEmpty()) {
43+
// Defer setting the authors property until the next try.
44+
return $config;
45+
}
46+
47+
return $this->authors = $this->parseConfigurationAuthors($config);
48+
}
49+
50+
protected function parseConfigurationAuthors(Collection $authors): Collection
51+
{
52+
return $authors->mapWithKeys(function (PostAuthor $author, string $username): array {
53+
return [$username ?: $author->username => $author];
54+
});
55+
}
56+
}

packages/framework/src/Foundation/HydeKernel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class HydeKernel implements SerializableContract
4545
use Concerns\ManagesExtensions;
4646
use Concerns\ManagesViewData;
4747
use Concerns\BootsHydeKernel;
48+
use Concerns\HasKernelData;
4849

4950
use Serializable;
5051
use Macroable;
@@ -110,6 +111,7 @@ public function toArray(): array
110111
'files' => $this->files(),
111112
'pages' => $this->pages(),
112113
'routes' => $this->routes(),
114+
'authors' => $this->authors(),
113115
];
114116
}
115117
}

packages/framework/src/Foundation/Internal/LoadYamlConfiguration.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Support\Arr;
88
use Hyde\Foundation\Application;
99
use Illuminate\Config\Repository;
10+
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
1011

1112
use function array_merge;
1213

@@ -43,6 +44,10 @@ public function bootstrap(Application $app): void
4344
protected function mergeParsedConfiguration(): void
4445
{
4546
foreach ($this->yaml->getData() as $namespace => $data) {
47+
if ($namespace === 'hyde' && isset($data['authors'])) {
48+
$data['authors'] = $this->parseAuthors($data['authors']);
49+
}
50+
4651
$this->mergeConfiguration($namespace, Arr::undot($data ?: []));
4752
}
4853
}
@@ -51,4 +56,15 @@ protected function mergeConfiguration(string $namespace, array $yaml): void
5156
{
5257
$this->config[$namespace] = array_merge($this->config[$namespace] ?? [], $yaml);
5358
}
59+
60+
/**
61+
* @param array<string, array{username?: string, name?: string, website?: string, bio?: string, avatar?: string, socials?: array<string, string>}> $authors
62+
* @return array<string, \Hyde\Framework\Features\Blogging\Models\PostAuthor>
63+
*/
64+
protected function parseAuthors(array $authors): array
65+
{
66+
return Arr::mapWithKeys($authors, function (array $author, string $username): array {
67+
return [$username => PostAuthor::getOrCreate($author)];
68+
});
69+
}
5470
}

packages/framework/src/Framework/Features/Blogging/Models/PostAuthor.php

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,37 @@
44

55
namespace Hyde\Framework\Features\Blogging\Models;
66

7+
use Hyde\Hyde;
78
use Stringable;
89
use Hyde\Facades\Author;
9-
use Hyde\Facades\Config;
10+
use Hyde\Pages\MarkdownPost;
1011
use Illuminate\Support\Collection;
11-
use JetBrains\PhpStorm\Deprecated;
12+
use Hyde\Support\Concerns\Serializable;
13+
use Hyde\Foundation\Kernel\PageCollection;
14+
use Hyde\Support\Contracts\SerializableContract;
1215

13-
use function strtolower;
1416
use function is_string;
17+
use function array_merge;
18+
use function array_filter;
1519

1620
/**
17-
* Object representation of a post author for the site.
21+
* Object representation of a blog post author for the site.
1822
*/
19-
class PostAuthor implements Stringable
23+
class PostAuthor implements Stringable, SerializableContract
2024
{
25+
use Serializable;
26+
2127
/**
2228
* The username of the author.
29+
*
2330
* This is the key used to find authors in the config.
2431
*/
2532
public readonly string $username;
2633

2734
/**
2835
* The display name of the author.
2936
*/
30-
public readonly string $name;
37+
public readonly ?string $name;
3138

3239
/**
3340
* The author's website URL.
@@ -37,6 +44,27 @@ class PostAuthor implements Stringable
3744
*/
3845
public readonly ?string $website;
3946

47+
/**
48+
* The author's biography.
49+
*/
50+
public readonly ?string $bio;
51+
52+
/**
53+
* The author's avatar image.
54+
*
55+
* If you in your Blade view use `Hyde::asset($author->avatar)`, then this value supports using both image names for files in `_media`, or full URIs starting with the protocol.
56+
*/
57+
public readonly ?string $avatar;
58+
59+
/**
60+
* The author's social media links/handles.
61+
*
62+
* @var array<string, string
63+
*
64+
* @example ['twitter' => 'mr_hyde'] ($service => $handle)
65+
*/
66+
public readonly array $socials;
67+
4068
/**
4169
* Construct a new Post Author object.
4270
*
@@ -45,58 +73,72 @@ class PostAuthor implements Stringable
4573
* @param string $username
4674
* @param string|null $name
4775
* @param string|null $website
76+
* @param string|null $bio
77+
* @param string|null $avatar
78+
* @param array<string, string> $socials
4879
*/
49-
public function __construct(string $username, ?string $name = null, ?string $website = null)
80+
public function __construct(string $username, ?string $name = null, ?string $website = null, ?string $bio = null, ?string $avatar = null, array $socials = [])
5081
{
5182
$this->username = $username;
52-
$this->name = $name ?? $this->username;
83+
$this->name = $name ?? $username;
5384
$this->website = $website;
85+
$this->bio = $bio;
86+
$this->avatar = $avatar;
87+
$this->socials = $socials;
5488
}
5589

5690
/**
5791
* Dynamically get or create an author based on a username string or front matter array.
5892
*
59-
* @param string|array{username?: string, name?: string, website?: string} $data
93+
* @param string|array{username?: string, name?: string, website?: string, bio?: string, avatar?: string, socials?: array<string, string>} $data
6094
*/
6195
public static function getOrCreate(string|array $data): static
6296
{
6397
if (is_string($data)) {
6498
return static::get($data);
6599
}
66100

67-
return Author::create(static::findUsername($data), $data['name'] ?? null, $data['website'] ?? null);
101+
return new static(...array_merge([
102+
'username' => static::findUsernameFromData($data),
103+
], $data));
68104
}
69105

70-
/** Get an Author from the config, or create it. */
106+
/** Get an Author from the config, or create it with the username. */
71107
public static function get(string $username): static
72108
{
73-
return static::all()->firstWhere('username', strtolower($username)) ?? Author::create($username);
109+
return static::all()->get($username) ?? Author::create($username);
74110
}
75111

76112
/** @return \Illuminate\Support\Collection<string, \Hyde\Framework\Features\Blogging\Models\PostAuthor> */
77113
public static function all(): Collection
78114
{
79-
return (new Collection(Config::getArray('hyde.authors', [])))->mapWithKeys(function (self $author): array {
80-
return [strtolower($author->username) => $author];
81-
});
115+
return Hyde::authors();
82116
}
83117

84118
public function __toString(): string
85119
{
86120
return $this->name;
87121
}
88122

123+
public function toArray(): array
124+
{
125+
return array_filter($this->automaticallySerialize());
126+
}
127+
89128
/**
90-
* @deprecated This is not needed as the name property can be accessed directly.
129+
* Get all posts by this author.
130+
*
131+
* @return \Hyde\Foundation\Kernel\PageCollection<\Hyde\Pages\MarkdownPost>
91132
*/
92-
#[Deprecated(reason: 'Use the name property instead.', replacement: '%class%->name')]
93-
public function getName(): string
133+
public function getPosts(): PageCollection
94134
{
95-
return $this->name;
135+
return MarkdownPost::getLatestPosts()->filter(function (MarkdownPost $post) {
136+
return $post->author?->username === $this->username;
137+
});
96138
}
97139

98140
/** @param array{username?: string, name?: string, website?: string} $data */
99-
protected static function findUsername(array $data): string
141+
protected static function findUsernameFromData(array $data): string
100142
{
101143
return $data['username'] ?? $data['name'] ?? 'Guest';
102144
}

packages/framework/src/Hyde.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
use Hyde\Enums\Feature;
88
use Hyde\Facades\Features;
99
use Hyde\Foundation\HydeKernel;
10+
use Illuminate\Support\Collection;
1011
use Hyde\Foundation\Kernel\FileCollection;
1112
use Hyde\Foundation\Kernel\Filesystem;
1213
use Hyde\Foundation\Kernel\PageCollection;
1314
use Hyde\Foundation\Kernel\RouteCollection;
15+
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
1416
use Hyde\Pages\Concerns\HydePage;
1517
use Hyde\Support\Models\Route;
1618
use Hyde\Support\Filesystem\SourceFile;
@@ -53,6 +55,7 @@
5355
* @method static string getMediaDirectory()
5456
* @method static string getMediaOutputDirectory()
5557
* @method static Features features()
58+
* @method static Collection<string, PostAuthor> authors()
5659
* @method static FileCollection<string, SourceFile> files()
5760
* @method static PageCollection<string, HydePage> pages()
5861
* @method static RouteCollection<string, Route> routes()

packages/framework/src/Markdown/Contracts/FrontMatter/SubSchemas/AuthorSchema.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@ interface AuthorSchema extends BlogPostSchema
1515
'name' => 'string',
1616
'username' => 'string',
1717
'website' => 'string',
18+
'bio' => 'string', // Todo: Support 'biography' as well?
19+
'avatar' => 'string',
20+
'socials' => 'array<string, string>',
1821
];
1922
}

0 commit comments

Comments
 (0)