Skip to content

Commit a41aa9e

Browse files
committed
Merge pull request #1798 from hydephp/reduce-author-username-duplication
[2.x] Breaking: Improve the new Authors API
2 parents d1445cc + d1927e5 commit a41aa9e

File tree

17 files changed

+594
-130
lines changed

17 files changed

+594
-130
lines changed

RELEASE_NOTES.md

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ This serves two purposes:
2121
- **Breaking:** The `hyde.features` configuration format has changed to use Enums instead of static method calls. For more information, see below.
2222
- **Breaking:** Renamed class `DataCollections` to `DataCollection`. For more information, see below.
2323
- **Breaking:** The `hyde.authors` config setting should now be keyed by the usernames. For more information, see below.
24+
- **Breaking:** The `Author::create()` method now returns an array instead of a `PostAuthor` instance. For more information, see below.
25+
- **Breaking:** The `Author::get()` method now returns `null` if an author is not found, rather than creating a new instance. For more information, see below.
2426
- Medium: The `route` function will now throw a `RouteNotFoundException` if the route does not exist in https://github.com/hydephp/develop/pull/1741
2527
- 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
2628
- 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.)
@@ -152,20 +154,50 @@ Of course, if you have disabled any of the features, do not include them in the
152154

153155
### Post Author changes
154156

155-
This release makes major improvements into the usability and design of the blog post author feature.
157+
This release makes major improvements to the usability and design of the blog post author feature.
156158

157159
Here is the full list of changes:
158160

159-
- Breaking: The `hyde.authors` config setting should now be keyed by the usernames
160-
- Removed: The deprecated `PostAuthor::getName()` method is now removed (use `$author->name`)
161-
- Feature: We now support setting authors in the YAML configuration! Fixes `#1719`
162-
- Feature: Added a `$author->getPosts()` method to get all author's posts
163-
- Feature: Authors now can also have custom biographies and social media links
164-
- The PostAuthor class is now Arrayable and JsonSerializable
165-
- The collection of site authors are now stored in the HydeKernel
166-
- Authors can additionally be accessed through `Hyde::authors()`
167-
168-
For more information, see https://github.com/hydephp/develop/pull/1782
161+
- Breaking: The `hyde.authors` config setting must now be keyed by the usernames, instead of providing the username in the author facade constructor.
162+
- Breaking: The `Author::create()` method now returns an array instead of a `PostAuthor` instance. This only affects custom code that uses the `Author` facade.
163+
- Breaking: The `Author::get()` method now returns `null` if an author is not found, rather than creating a new instance. This only affects custom code that uses the `Author` facade.
164+
- Removed: The deprecated `PostAuthor::getName()` method has been removed (use `$author->name` instead).
165+
- Changed: Author usernames are now automatically normalized (converted to lowercase and spaces replaced with underscores in order to ensure URL routability).
166+
- Changed: If an author display name is not provided, it is now intelligently generated from the username.
167+
- Feature: Authors can now be set in the YAML configuration.
168+
- Feature: Added a `$author->getPosts()` method to get all of an author's posts.
169+
- Feature: Authors now support custom biographies, avatars, and social media links. Note that these are not currently used in any of the default templates, but you can use them in your custom views.
170+
- The collection of site authors is now stored in the HydeKernel, meaning authors can be accessed through `Hyde::authors()`.
171+
- The `PostAuthor` class is now Arrayable and JsonSerializable.
172+
173+
#### Upgrade guide:
174+
175+
1. Update your `config/hyde.php` file to use the new author configuration format:
176+
```php
177+
'authors' => [
178+
'username' => Author::create(
179+
name: 'Display Name',
180+
website: 'https://example.com',
181+
bio: 'Author bio',
182+
avatar: 'avatar.png',
183+
socials: ['twitter' => '@username']
184+
),
185+
],
186+
```
187+
188+
2. Review and update any code that uses the `Author` facade:
189+
- The `create()` method now returns an array instead of a `PostAuthor` instance.
190+
- The `get()` method may return `null`, so handle this case in your code.
191+
192+
3. Check your blog post front matter and ensure that `author` fields match the new username keys in your configuration.
193+
194+
4. If you have custom templates that use author data, update them to:
195+
- Optional: Feel free to use the new available fields: `bio`, `avatar`, and `socials`.
196+
- Account for usernames now being lowercase with underscores which may lead to changed HTML or URL paths.
197+
198+
5. If you were relying on `Author::get()` to create new authors on the fly, update your code to handle `null` returns or create authors explicitly.
199+
200+
For more information, see https://github.com/hydephp/develop/pull/1782 and https://github.com/hydephp/develop/pull/1798
169201

170202
### Documentation search page changes
171203

config/hyde.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,26 @@
277277
| However, it's tedious to have to add those to each and every
278278
| post you make, and keeping them updated is even harder.
279279
|
280-
| Here you can add predefined authors. When writing posts,
281-
| just specify the username in the front matter, and the
282-
| rest of the data will be pulled from a matching entry.
280+
| To solve this problem, you can add predefined authors with this setting.
281+
| When writing posts just specify the author's username (the array key).
282+
| Hyde will pull the matching data from here and fill in the blanks.
283283
|
284284
*/
285285

286286
'authors' => [
287287
'mr_hyde' => Author::create(
288-
'mr_hyde', // Required username
289-
'Mr. Hyde', // Optional display name
290-
'https://hydephp.com' // Optional website URL
288+
// The following settings are used in the default blog post template.
289+
name: 'Mr. Hyde', // Optional display name
290+
website: 'https://hydephp.com', // Optional website URL
291+
292+
// The following settings are not used in the bundled templates,
293+
// but you can use them in your own custom views, for example.
294+
// bio: 'The mysterious author of HydePHP',
295+
// avatar: 'avatar.png',
296+
// socials: [
297+
// 'twitter' => 'HydeFramework',
298+
// 'github' => 'hydephp',
299+
// ],
291300
),
292301
],
293302

docs/creating-content/blog-posts.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,23 @@ author: mr_hyde
150150

151151
```yaml
152152
author:
153+
# These are used in the default author templates
153154
name: "Mr. Hyde"
154155
username: mr_hyde
155156
website: https://twitter.com/HydeFramework
157+
158+
# These are not used in the default author templates, but can be used in your custom views
159+
bio: "The mysterious author of HydePHP"
160+
avatar: avatar.png
161+
socials:
162+
twitter: "@HydeFramework"
163+
github: "hydephp"
156164
```
157165

158-
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, if one exists.
166+
167+
When using an array, you don't need to include all properties. Specified values will override the corresponding entries in the `authors` config.
168+
169+
Note: Author usernames are automatically normalized (converted to lowercase with spaces replaced by underscores).
160170

161171
### Image
162172

docs/digging-deeper/customization.md

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,21 +124,32 @@ Here are the default settings:
124124
125125
### Authors
126126

127-
Hyde has support for adding authors in front matter, for example to automatically add a link to your website or social media profiles.
128-
However, it's tedious to have to add those to each and every post you make, and keeping them updated is even harder.
127+
Hyde supports adding authors to blog posts, allowing you to automatically include information like display names and website links.
128+
We even support fields for avatars, biographies, and social media profiles, which you can use in your custom Blade templates.
129129

130+
While you can set all this data directly in the [front matter](blog-posts#author), that quickly becomes tedious and hard to maintain.
130131
Instead, you can predefine authors in the Hyde config. When writing posts, just specify the username in the front matter,
131132
and the rest of the data will be pulled from a matching entry found in the configuration file.
132133

133-
#### Example
134+
#### Configuration
135+
136+
Authors are defined in the `config/hyde.php` file under the `authors` key. Each author is keyed by their username and is configured using the `Author::create()` method:
134137

135138
```php
136139
// filepath: config/hyde.php
137140
'authors' => [
138-
Author::create(
139-
username: 'mr_hyde', // Required username
140-
name: 'Mr. Hyde', // Optional display name
141-
website: 'https://hydephp.com' // Optional website URL
141+
'mr_hyde' => Author::create(
142+
// The following fields, along with the username, are used by the default blog post templates.
143+
name: 'Mr. Hyde',
144+
website: 'https://hydephp.com',
145+
146+
// These fields are not currently used in the default templates, but you can use them in your custom views.
147+
bio: 'The mysterious author of HydePHP',
148+
avatar: 'avatar.png',
149+
socials: [
150+
'twitter' => '@HydeFramework',
151+
'github' => 'hydephp',
152+
],
142153
),
143154
],
144155
```
@@ -150,14 +161,38 @@ author:
150161
username: mr_hyde
151162
name: Mr. Hyde
152163
website: https://hydephp.com
164+
bio: The mysterious author of HydePHP
165+
avatar: avatar.png
166+
socials:
167+
twitter: "@HydeFramework"
168+
github: hydephp
153169
```
154170
155-
But you only have to specify the username:
171+
But you only have to specify the username to get all the other data.
156172
157173
```yaml
158174
author: mr_hyde
159175
```
160176
177+
If you want to override the data for a specific post, you can do so in the [front matter](blog-posts#author) which is great for guest authors or one-off posts.
178+
179+
180+
#### Available Fields
181+
182+
- `name`: The author's display name (optional, generated from username if not provided)
183+
- `website`: The author's website URL (optional)
184+
- `bio`: A short biography (optional, not used in default templates)
185+
- `avatar`: Path to the author's avatar image (optional, not used in default templates)
186+
- `socials`: An array of social media links (optional, not used in default templates)
187+
188+
#### Notes
189+
190+
- Usernames are automatically normalized (converted to lowercase with spaces replaced by underscores)
191+
- The `PostAuthor` class includes a `getPosts()` method to retrieve all posts by an author
192+
- Authors can be accessed through `Hyde::authors()`
193+
194+
For more advanced usage and customization, refer to the [source code](https://github.com/hydephp/framework/blob/master/src/Framework/Features/Blogging/Models/PostAuthor.php) which is well documented.
195+
161196
### Footer
162197

163198
Most websites have a footer with copyright details and contact information. You probably want to change the Markdown to include your information, though you are of course welcome to keep the default attribution link!

packages/framework/config/hyde.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,26 @@
277277
| However, it's tedious to have to add those to each and every
278278
| post you make, and keeping them updated is even harder.
279279
|
280-
| Here you can add predefined authors. When writing posts,
281-
| just specify the username in the front matter, and the
282-
| rest of the data will be pulled from a matching entry.
280+
| To solve this problem, you can add predefined authors with this setting.
281+
| When writing posts just specify the author's username (the array key).
282+
| Hyde will pull the matching data from here and fill in the blanks.
283283
|
284284
*/
285285

286286
'authors' => [
287287
'mr_hyde' => Author::create(
288-
'mr_hyde', // Required username
289-
'Mr. Hyde', // Optional display name
290-
'https://hydephp.com' // Optional website URL
288+
// The following settings are used in the default blog post template.
289+
name: 'Mr. Hyde', // Optional display name
290+
website: 'https://hydephp.com', // Optional website URL
291+
292+
// The following settings are not used in the bundled templates,
293+
// but you can use them in your own custom views, for example.
294+
// bio: 'The mysterious author of HydePHP',
295+
// avatar: 'avatar.png',
296+
// socials: [
297+
// 'twitter' => 'HydeFramework',
298+
// 'github' => 'hydephp',
299+
// ],
291300
),
292301
],
293302

packages/framework/src/Facades/Author.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,27 @@
1515
class Author
1616
{
1717
/**
18-
* Construct a new Post Author. For Hyde to discover this author,
19-
* you must call this method from your hyde.php config file.
18+
* Configuration helper method to define a new blog post author, with better IDE support.
19+
*
20+
* The returned array will then be used by the framework to create a new PostAuthor instance.
2021
*
2122
* @see https://hydephp.com/docs/1.x/customization.html#authors
2223
*
23-
* @param string $username The username of the author. This is the key used to find authors in the config.
2424
* @param string|null $name The optional display name of the author, leave blank to use the username.
2525
* @param string|null $website The author's optional website URL. Website, Twitter, etc.
26+
* @param string|null $bio The author's optional biography text. Markdown supported.
27+
* @param string|null $avatar The author's optional avatar image. Supports both image names and full URIs.
28+
* @param array<string, string>|null $socials The author's optional social media links/handles.
2629
*/
27-
public static function create(string $username, ?string $name = null, ?string $website = null): PostAuthor
30+
public static function create(?string $name = null, ?string $website = null, ?string $bio = null, ?string $avatar = null, ?array $socials = null): array
2831
{
29-
return new PostAuthor($username, $name, $website);
32+
return compact('name', 'website', 'bio', 'avatar', 'socials');
3033
}
3134

3235
/**
33-
* Get a Post Author instance from the config. If no author matching the username is found,
34-
* a new Post Author instance will be created with just username supplied to the method.
36+
* Get a Post Author instance by username, or null if not found.
3537
*/
36-
public static function get(string $username): PostAuthor
38+
public static function get(string $username): ?PostAuthor
3739
{
3840
return PostAuthor::get($username);
3941
}

packages/framework/src/Foundation/Concerns/HasKernelData.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Hyde\Facades\Config;
88
use Illuminate\Support\Collection;
99
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
10+
use Hyde\Framework\Exceptions\InvalidConfigurationException;
1011

1112
use function collect;
1213

@@ -49,8 +50,16 @@ public function authors(): Collection
4950

5051
protected function parseConfigurationAuthors(Collection $authors): Collection
5152
{
52-
return $authors->mapWithKeys(function (PostAuthor $author, string $username): array {
53-
return [$username ?: $author->username => $author];
53+
return $authors->mapWithKeys(function (array $author, string $username): array {
54+
if (! $username) {
55+
throw new InvalidConfigurationException('Author username cannot be empty. Did you forget to set the author\'s array key?', 'hyde', 'authors');
56+
}
57+
58+
$username = PostAuthor::normalizeUsername($username);
59+
60+
$author['username'] = $username;
61+
62+
return [$username => PostAuthor::create($author)];
5463
});
5564
}
5665
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
namespace Hyde\Foundation\Internal;
66

7+
use Throwable;
78
use Illuminate\Support\Arr;
89
use Hyde\Foundation\Application;
910
use Illuminate\Config\Repository;
1011
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
12+
use Hyde\Framework\Exceptions\InvalidConfigurationException;
1113

1214
use function array_merge;
1315

@@ -64,7 +66,14 @@ protected function mergeConfiguration(string $namespace, array $yaml): void
6466
protected function parseAuthors(array $authors): array
6567
{
6668
return Arr::mapWithKeys($authors, function (array $author, string $username): array {
67-
return [$username => PostAuthor::getOrCreate($author)];
69+
try {
70+
return [$username => PostAuthor::create($author)];
71+
} catch (Throwable $exception) {
72+
throw new InvalidConfigurationException(
73+
'Invalid author configuration detected in the YAML config file. Please double check the syntax.',
74+
previous: $exception
75+
);
76+
}
6877
});
6978
}
7079
}

packages/framework/src/Framework/Factories/BlogPostDataFactory.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Hyde\Markdown\Models\Markdown;
1515
use Hyde\Support\Models\DateString;
1616

17+
use function is_string;
18+
1719
/**
1820
* Streamlines the data construction specific to a blog post.
1921
*
@@ -91,7 +93,7 @@ protected function makeDate(): ?DateString
9193
protected function makeAuthor(): ?PostAuthor
9294
{
9395
if ($this->getMatter('author')) {
94-
return PostAuthor::getOrCreate($this->getMatter('author'));
96+
return $this->getOrCreateAuthor();
9597
}
9698

9799
return null;
@@ -111,6 +113,17 @@ private function makeDescriptionFromMarkdownBody(): string
111113
return Str::limit((new ConvertsMarkdownToPlainText($this->markdown->body()))->execute(), 125);
112114
}
113115

116+
private function getOrCreateAuthor(): PostAuthor
117+
{
118+
$data = $this->getMatter('author');
119+
120+
if (is_string($data)) {
121+
return PostAuthor::get($data) ?? PostAuthor::create(['username' => $data]);
122+
}
123+
124+
return PostAuthor::create($data);
125+
}
126+
114127
protected function getMatter(string $key): string|null|array
115128
{
116129
/** @var string|null|array $value */

0 commit comments

Comments
 (0)