Skip to content

Commit

Permalink
Merge pull request #1798 from hydephp/reduce-author-username-duplication
Browse files Browse the repository at this point in the history
[2.x] Breaking: Improve the new Authors API
  • Loading branch information
caendesilva authored Jul 9, 2024
2 parents a0bc7e2 + d1927e5 commit 0e42c18
Show file tree
Hide file tree
Showing 17 changed files with 594 additions and 130 deletions.
54 changes: 43 additions & 11 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ This serves two purposes:
- **Breaking:** The `hyde.features` configuration format has changed to use Enums instead of static method calls. For more information, see below.
- **Breaking:** Renamed class `DataCollections` to `DataCollection`. For more information, see below.
- **Breaking:** The `hyde.authors` config setting should now be keyed by the usernames. For more information, see below.
- **Breaking:** The `Author::create()` method now returns an array instead of a `PostAuthor` instance. For more information, see below.
- **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.
- Medium: The `route` function will now throw a `RouteNotFoundException` if the route does not exist in https://github.com/hydephp/develop/pull/1741
- 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
- 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.)
Expand Down Expand Up @@ -152,20 +154,50 @@ Of course, if you have disabled any of the features, do not include them in the

### Post Author changes

This release makes major improvements into the usability and design of the blog post author feature.
This release makes major improvements to the usability and design of the blog post author feature.

Here is the full list of changes:

- Breaking: The `hyde.authors` config setting should now be keyed by the usernames
- Removed: The deprecated `PostAuthor::getName()` method is now removed (use `$author->name`)
- Feature: We now support setting authors in the YAML configuration! Fixes `#1719`
- Feature: Added a `$author->getPosts()` method to get all author's posts
- Feature: Authors now can also have custom biographies and social media links
- The PostAuthor class is now Arrayable and JsonSerializable
- The collection of site authors are now stored in the HydeKernel
- Authors can additionally be accessed through `Hyde::authors()`

For more information, see https://github.com/hydephp/develop/pull/1782
- Breaking: The `hyde.authors` config setting must now be keyed by the usernames, instead of providing the username in the author facade constructor.
- Breaking: The `Author::create()` method now returns an array instead of a `PostAuthor` instance. This only affects custom code that uses the `Author` facade.
- 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.
- Removed: The deprecated `PostAuthor::getName()` method has been removed (use `$author->name` instead).
- Changed: Author usernames are now automatically normalized (converted to lowercase and spaces replaced with underscores in order to ensure URL routability).
- Changed: If an author display name is not provided, it is now intelligently generated from the username.
- Feature: Authors can now be set in the YAML configuration.
- Feature: Added a `$author->getPosts()` method to get all of an author's posts.
- 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.
- The collection of site authors is now stored in the HydeKernel, meaning authors can be accessed through `Hyde::authors()`.
- The `PostAuthor` class is now Arrayable and JsonSerializable.

#### Upgrade guide:

1. Update your `config/hyde.php` file to use the new author configuration format:
```php
'authors' => [
'username' => Author::create(
name: 'Display Name',
website: 'https://example.com',
bio: 'Author bio',
avatar: 'avatar.png',
socials: ['twitter' => '@username']
),
],
```

2. Review and update any code that uses the `Author` facade:
- The `create()` method now returns an array instead of a `PostAuthor` instance.
- The `get()` method may return `null`, so handle this case in your code.

3. Check your blog post front matter and ensure that `author` fields match the new username keys in your configuration.

4. If you have custom templates that use author data, update them to:
- Optional: Feel free to use the new available fields: `bio`, `avatar`, and `socials`.
- Account for usernames now being lowercase with underscores which may lead to changed HTML or URL paths.

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.

For more information, see https://github.com/hydephp/develop/pull/1782 and https://github.com/hydephp/develop/pull/1798

### Documentation search page changes

Expand Down
21 changes: 15 additions & 6 deletions config/hyde.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,17 +277,26 @@
| However, it's tedious to have to add those to each and every
| post you make, and keeping them updated is even harder.
|
| Here you can add predefined authors. When writing posts,
| just specify the username in the front matter, and the
| rest of the data will be pulled from a matching entry.
| To solve this problem, you can add predefined authors with this setting.
| When writing posts just specify the author's username (the array key).
| Hyde will pull the matching data from here and fill in the blanks.
|
*/

'authors' => [
'mr_hyde' => Author::create(
'mr_hyde', // Required username
'Mr. Hyde', // Optional display name
'https://hydephp.com' // Optional website URL
// The following settings are used in the default blog post template.
name: 'Mr. Hyde', // Optional display name
website: 'https://hydephp.com', // Optional website URL

// The following settings are not used in the bundled templates,
// but you can use them in your own custom views, for example.
// bio: 'The mysterious author of HydePHP',
// avatar: 'avatar.png',
// socials: [
// 'twitter' => 'HydeFramework',
// 'github' => 'hydephp',
// ],
),
],

Expand Down
14 changes: 12 additions & 2 deletions docs/creating-content/blog-posts.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,23 @@ author: mr_hyde

```yaml
author:
# These are used in the default author templates
name: "Mr. Hyde"
username: mr_hyde
website: https://twitter.com/HydeFramework
# These are not used in the default author templates, but can be used in your custom views
bio: "The mysterious author of HydePHP"
avatar: avatar.png
socials:
twitter: "@HydeFramework"
github: "hydephp"
```

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

When using an array, you don't need to include all properties. Specified values will override the corresponding entries in the `authors` config.

Note: Author usernames are automatically normalized (converted to lowercase with spaces replaced by underscores).

### Image

Expand Down
51 changes: 43 additions & 8 deletions docs/digging-deeper/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,32 @@ Here are the default settings:
### Authors

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

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

#### Example
#### Configuration

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:

```php
// filepath: config/hyde.php
'authors' => [
Author::create(
username: 'mr_hyde', // Required username
name: 'Mr. Hyde', // Optional display name
website: 'https://hydephp.com' // Optional website URL
'mr_hyde' => Author::create(
// The following fields, along with the username, are used by the default blog post templates.
name: 'Mr. Hyde',
website: 'https://hydephp.com',

// These fields are not currently used in the default templates, but you can use them in your custom views.
bio: 'The mysterious author of HydePHP',
avatar: 'avatar.png',
socials: [
'twitter' => '@HydeFramework',
'github' => 'hydephp',
],
),
],
```
Expand All @@ -150,14 +161,38 @@ author:
username: mr_hyde
name: Mr. Hyde
website: https://hydephp.com
bio: The mysterious author of HydePHP
avatar: avatar.png
socials:
twitter: "@HydeFramework"
github: hydephp
```
But you only have to specify the username:
But you only have to specify the username to get all the other data.
```yaml
author: mr_hyde
```
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.
#### Available Fields
- `name`: The author's display name (optional, generated from username if not provided)
- `website`: The author's website URL (optional)
- `bio`: A short biography (optional, not used in default templates)
- `avatar`: Path to the author's avatar image (optional, not used in default templates)
- `socials`: An array of social media links (optional, not used in default templates)

#### Notes

- Usernames are automatically normalized (converted to lowercase with spaces replaced by underscores)
- The `PostAuthor` class includes a `getPosts()` method to retrieve all posts by an author
- Authors can be accessed through `Hyde::authors()`

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.

### Footer

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!
Expand Down
21 changes: 15 additions & 6 deletions packages/framework/config/hyde.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,17 +277,26 @@
| However, it's tedious to have to add those to each and every
| post you make, and keeping them updated is even harder.
|
| Here you can add predefined authors. When writing posts,
| just specify the username in the front matter, and the
| rest of the data will be pulled from a matching entry.
| To solve this problem, you can add predefined authors with this setting.
| When writing posts just specify the author's username (the array key).
| Hyde will pull the matching data from here and fill in the blanks.
|
*/

'authors' => [
'mr_hyde' => Author::create(
'mr_hyde', // Required username
'Mr. Hyde', // Optional display name
'https://hydephp.com' // Optional website URL
// The following settings are used in the default blog post template.
name: 'Mr. Hyde', // Optional display name
website: 'https://hydephp.com', // Optional website URL

// The following settings are not used in the bundled templates,
// but you can use them in your own custom views, for example.
// bio: 'The mysterious author of HydePHP',
// avatar: 'avatar.png',
// socials: [
// 'twitter' => 'HydeFramework',
// 'github' => 'hydephp',
// ],
),
],

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

/**
* Get a Post Author instance from the config. If no author matching the username is found,
* a new Post Author instance will be created with just username supplied to the method.
* Get a Post Author instance by username, or null if not found.
*/
public static function get(string $username): PostAuthor
public static function get(string $username): ?PostAuthor
{
return PostAuthor::get($username);
}
Expand Down
13 changes: 11 additions & 2 deletions packages/framework/src/Foundation/Concerns/HasKernelData.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Hyde\Facades\Config;
use Illuminate\Support\Collection;
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
use Hyde\Framework\Exceptions\InvalidConfigurationException;

use function collect;

Expand Down Expand Up @@ -49,8 +50,16 @@ public function authors(): Collection

protected function parseConfigurationAuthors(Collection $authors): Collection
{
return $authors->mapWithKeys(function (PostAuthor $author, string $username): array {
return [$username ?: $author->username => $author];
return $authors->mapWithKeys(function (array $author, string $username): array {
if (! $username) {
throw new InvalidConfigurationException('Author username cannot be empty. Did you forget to set the author\'s array key?', 'hyde', 'authors');
}

$username = PostAuthor::normalizeUsername($username);

$author['username'] = $username;

return [$username => PostAuthor::create($author)];
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

namespace Hyde\Foundation\Internal;

use Throwable;
use Illuminate\Support\Arr;
use Hyde\Foundation\Application;
use Illuminate\Config\Repository;
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
use Hyde\Framework\Exceptions\InvalidConfigurationException;

use function array_merge;

Expand Down Expand Up @@ -64,7 +66,14 @@ protected function mergeConfiguration(string $namespace, array $yaml): void
protected function parseAuthors(array $authors): array
{
return Arr::mapWithKeys($authors, function (array $author, string $username): array {
return [$username => PostAuthor::getOrCreate($author)];
try {
return [$username => PostAuthor::create($author)];
} catch (Throwable $exception) {
throw new InvalidConfigurationException(
'Invalid author configuration detected in the YAML config file. Please double check the syntax.',
previous: $exception
);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use Hyde\Markdown\Models\Markdown;
use Hyde\Support\Models\DateString;

use function is_string;

/**
* Streamlines the data construction specific to a blog post.
*
Expand Down Expand Up @@ -91,7 +93,7 @@ protected function makeDate(): ?DateString
protected function makeAuthor(): ?PostAuthor
{
if ($this->getMatter('author')) {
return PostAuthor::getOrCreate($this->getMatter('author'));
return $this->getOrCreateAuthor();
}

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

private function getOrCreateAuthor(): PostAuthor
{
$data = $this->getMatter('author');

if (is_string($data)) {
return PostAuthor::get($data) ?? PostAuthor::create(['username' => $data]);
}

return PostAuthor::create($data);
}

protected function getMatter(string $key): string|null|array
{
/** @var string|null|array $value */
Expand Down
Loading

0 comments on commit 0e42c18

Please sign in to comment.