Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.x] DataCollections improvements #1732

Merged
merged 122 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
10c2809
Merge branch 'master' into 2.x-dev
caendesilva Jun 23, 2024
a2f4219
Add additional fixture for test
caendesilva Jun 23, 2024
71e2212
Test YAML collections without triple dashes
caendesilva Jun 23, 2024
ff89641
Support parsing Yaml data files with both dashes and without
caendesilva Jun 23, 2024
399d447
Merge branch '2.x-dev' into 2x-datacollections-improvements
caendesilva Jun 23, 2024
30b6365
Yaml data files no longer need to start with triple dashes
caendesilva Jun 23, 2024
5b2af72
Simplify Yaml data file parsing
caendesilva Jun 23, 2024
e4e6808
Update RELEASE_NOTES.md
caendesilva Jun 23, 2024
5d655cb
Update RELEASE_NOTES.md
caendesilva Jun 23, 2024
daa7a3a
Revert "Update RELEASE_NOTES.md"
caendesilva Jun 23, 2024
6ebc9c9
Unwrap unnecessary condition
caendesilva Jun 23, 2024
e39067e
Merge branch '2.x-dev' into 2x-datacollections-improvements
caendesilva Jun 23, 2024
b3807fb
Merge branch '2.x-dev' into 2x-datacollections-improvements
caendesilva Jun 23, 2024
22c4b66
No longer automatically create the data collections directory
caendesilva Jun 23, 2024
4245ba5
Breaking: Rename class `DataCollections` to `DataCollection`
caendesilva Jun 23, 2024
a24aa34
Add mockable data collection test class
caendesilva Jun 24, 2024
02b8b06
Get by name
caendesilva Jun 24, 2024
6c91c63
Normalize file contents syntax
caendesilva Jun 24, 2024
dd63ffe
Add array based glob helper
caendesilva Jun 24, 2024
b577a48
Collect using array glob
caendesilva Jun 24, 2024
db62861
Annotate generics
caendesilva Jun 24, 2024
dd698ab
Assert generics and test mock setup
caendesilva Jun 24, 2024
d9bb52a
Mock the filesystem
caendesilva Jun 24, 2024
e8af560
Simplify testing helpers
caendesilva Jun 24, 2024
ba3b72a
Test Markdown method returns collection of Markdown documents
caendesilva Jun 24, 2024
8474481
Test Yaml method returns collection of front matter objects
caendesilva Jun 24, 2024
07e9cc3
Update mocks to better match real setup
caendesilva Jun 24, 2024
330940e
Fix quote usage
caendesilva Jun 24, 2024
c2ad0e9
Test differing Yaml syntaxes
caendesilva Jun 24, 2024
ae8523d
Test Yaml fault tolerance
caendesilva Jun 24, 2024
bc8c4fc
Extract helper to assert front matter collection structure
caendesilva Jun 24, 2024
d36c1a7
Add helper to assert Json collection structure
caendesilva Jun 24, 2024
46a2bb1
Simplify assertion handling
caendesilva Jun 24, 2024
7535cdc
Test Json collection handling
caendesilva Jun 24, 2024
9ce2c98
Remove duplicated word from documentation
caendesilva Jun 24, 2024
d491da1
Refactor to extract helper method
caendesilva Jun 24, 2024
aaffe2c
Move up helper method
caendesilva Jun 24, 2024
194899d
Rename callback parameter
caendesilva Jun 24, 2024
f89f408
Refactor to further extract shared logic to helper
caendesilva Jun 24, 2024
a80b3d4
Add null return type support pending https://github.com/hydephp/devel…
caendesilva Jun 24, 2024
ca6f87d
Annotate helper method generics
caendesilva Jun 24, 2024
0b54e3d
Cleanup PHPDoc comment
caendesilva Jun 24, 2024
c232397
Annotate helper method generics
caendesilva Jun 24, 2024
1edcc24
Inline local variable
caendesilva Jun 24, 2024
308a9c1
Reorder newlines
caendesilva Jun 24, 2024
c2f0faa
Normalize PHPDoc types
caendesilva Jun 24, 2024
24532a1
Introduce local variable
caendesilva Jun 24, 2024
25b4aeb
Update Data Collections to validate JSON data files
caendesilva Jun 24, 2024
ebebaa1
Convert string interpolation to a 'sprintf()' call
caendesilva Jun 24, 2024
06dfada
Display the error that caused the JSON decoding to fail
caendesilva Jun 24, 2024
9702722
Document example to help issue searching lead to upgrade path
caendesilva Jun 24, 2024
b217795
Throw a normalized exception for issues parsing Yaml
caendesilva Jun 25, 2024
d08b187
Throw parse exception for empty Yaml to match `json_validate`
caendesilva Jun 25, 2024
f93cb0e
Document code reasoning
caendesilva Jun 25, 2024
b0d1e22
Remove unreachable null coalesce
caendesilva Jun 25, 2024
65aadb5
Trim trailing periods to make message fit in better
caendesilva Jun 25, 2024
d4c970e
Import used functions
caendesilva Jun 25, 2024
9f52220
Extract helper methods responsible for parsing each file
caendesilva Jun 25, 2024
2d2cfe8
Convert simplified closures to arrow functions
caendesilva Jun 25, 2024
a99947a
Document throws
caendesilva Jun 25, 2024
df820ef
Move return into try/catch block
caendesilva Jun 25, 2024
9b33cfd
Inline local variable
caendesilva Jun 25, 2024
b933be6
Cleanup formatting
caendesilva Jun 25, 2024
fc67b11
Use first class callable syntax where possible
caendesilva Jun 25, 2024
88a9204
Support passing extra arguments to discover method
caendesilva Jun 25, 2024
dc26d88
Clarify parameter name
caendesilva Jun 25, 2024
ea04ed1
Narrow down mixed generic return type to actual supported types
caendesilva Jun 25, 2024
670dd7a
Extract testing helper
caendesilva Jun 25, 2024
e32f6f8
Update testing helper to compare front matter
caendesilva Jun 25, 2024
6c1497b
Support mixed states
caendesilva Jun 25, 2024
8be9527
Test Markdown collections with front matter
caendesilva Jun 25, 2024
bde59d8
Add edge case tests
caendesilva Jun 25, 2024
11a9205
Fix typo in testing helper name
caendesilva Jun 25, 2024
30c2a74
Test Markdown method with invalid front matter
caendesilva Jun 25, 2024
9ca7a79
Normalize exception handling for Markdown data files
caendesilva Jun 25, 2024
4ecabc9
Throw an exception when Markdown data file is empty
caendesilva Jun 25, 2024
005dc15
Reorder test methods
caendesilva Jun 25, 2024
ab762b9
Remove unnecessary trim already run in blank function
caendesilva Jun 25, 2024
6d81d41
Cleanup code
caendesilva Jun 25, 2024
5cf056d
Introduce local variable
caendesilva Jun 25, 2024
6ffd6f9
Refactor to have single try/catch block in loop
caendesilva Jun 25, 2024
415a32a
Simplify and cleanup exception file type label handing
caendesilva Jun 25, 2024
558f407
Create ParseException.php
caendesilva Jun 25, 2024
86c6147
Throw our ParseException instead of InvalidArgumentException
caendesilva Jun 25, 2024
1c7114a
Improve wording
caendesilva Jun 25, 2024
c5bc374
Simplify type label generator to use the file extension
caendesilva Jun 25, 2024
260ce22
Fix parse exception namespace
caendesilva Jun 25, 2024
cf953e6
Fix class name capitalization
caendesilva Jun 25, 2024
2783e0d
Document exception scope
caendesilva Jun 25, 2024
54285af
Revert "Document exception scope"
caendesilva Jun 25, 2024
2fba023
Mark ParseException class as experimental
caendesilva Jun 25, 2024
d2829f2
Test covers ParseException
caendesilva Jun 25, 2024
7f4d48d
Override parent constructor
caendesilva Jun 25, 2024
7960357
Unit test covers ParseException
caendesilva Jun 25, 2024
e5d4c82
Refactor to move exception message assembly to custom exception
caendesilva Jun 25, 2024
f4e18ba
Add unit tests for custom ParseException
caendesilva Jun 25, 2024
301c44c
Improve parse exception message assembly
caendesilva Jun 25, 2024
fcab984
Revert "Unit test covers ParseException"
caendesilva Jun 25, 2024
c604c93
Test parse exception code
caendesilva Jun 25, 2024
eaa3045
Remove unused use statement
caendesilva Jun 25, 2024
4348f1c
Set parse exception status code to 422
caendesilva Jun 25, 2024
5fb82c1
Replace string replacement with match expression
caendesilva Jun 25, 2024
ad15064
Add source code spacing
caendesilva Jun 25, 2024
05910f3
Update RELEASE_NOTES.md
caendesilva Jun 25, 2024
68b096d
Extract helper method
caendesilva Jun 25, 2024
19ac8df
Inline local variable
caendesilva Jun 25, 2024
5393e36
Extract helper method
caendesilva Jun 25, 2024
6fc3d21
Move up method in source
caendesilva Jun 25, 2024
177185b
Inline local variables
caendesilva Jun 25, 2024
770df88
Convert a 'sprintf()' call to string interpolation
caendesilva Jun 25, 2024
e1ce0c3
Revert "Convert a 'sprintf()' call to string interpolation"
caendesilva Jun 25, 2024
3d05b2f
Split comma-separated values into multiple lines
caendesilva Jun 25, 2024
b2d3282
Extract helper method
caendesilva Jun 25, 2024
323abb1
Move up method in source
caendesilva Jun 25, 2024
8fba6af
Conditionally assemble file path string
caendesilva Jun 25, 2024
694b1cd
Refactor and cleanup code
caendesilva Jun 25, 2024
f1e82a1
Remove unnecessary parentheses
caendesilva Jun 25, 2024
e976cee
Revert "Remove unnecessary parentheses"
caendesilva Jun 25, 2024
d6a619f
Update RELEASE_NOTES.md
caendesilva Jun 25, 2024
b2912a3
Draft upcoming change notes
caendesilva Jun 25, 2024
227db4f
Revert "Draft upcoming change notes"
caendesilva Jun 25, 2024
0a3a65a
Clean up release notes
caendesilva Jun 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@ This serves two purposes:
### Added
- You can now specify navigation priorities by adding a numeric prefix to the source file names in https://github.com/hydephp/develop/pull/1709
- Added a new `\Hyde\Framework\Actions\PreBuildTasks\TransferMediaAssets` build task handle media assets transfers for site builds.
- Added a new `\Hyde\Framework\Exceptions\ParseException` exception class to handle parsing exceptions in data collection files in https://github.com/hydephp/develop/pull/1732
- The `\Hyde\Facades\Features` class is no longer marked as internal, and is now thus part of the public API.

### Changed
- **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.
- **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.
- 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.)
- Minor: The documentation article component now supports disabling the semantic rendering using a falsy value in https://github.com/hydephp/develop/pull/1566
- Minor: Changed the default build task message to make it more concise in https://github.com/hydephp/develop/pull/1659
- Minor: Data collection files are now validated for syntax errors during discovery in https://github.com/hydephp/develop/pull/1732
- The `hasFeature` method on the Hyde facade and HydeKernel now only accepts a Feature enum value instead of a string for its parameter.
- Changed how the documentation search is generated, to be an `InMemoryPage` instead of a post-build task.
- Media asset files are now copied using the new build task instead of the deprecated `BuildService::transferMediaAssets()` method.
- Calling the `Include::path()` method will no longer create the includes directory in https://github.com/hydephp/develop/pull/1707

- Calling the `DataCollection` methods will no longer create the data collections directory in https://github.com/hydephp/develop/pull/1732

### Deprecated
- for soon-to-be removed features.
Expand Down Expand Up @@ -173,6 +176,7 @@ Due to the scope of the rewrite, the easiest and fastest way to upgrade your cod

- For a full comparison of the changes, you may see the PR that introduced the new API: https://github.com/hydephp/develop/pull/1568/files
- For information on how to use the new Navigation API, see the documentation: https://hydephp.com/docs/2.x/navigation-api
- If you use DataCollections, you should read the upgrade path below as there are breaking changes to the DataCollection API.

### HTML ID changes

Expand Down Expand Up @@ -216,3 +220,33 @@ For example, if you triggered the media transfer with a build service method cal

(new TransferMediaAssets())->run();
```

### DataCollection API changes

The DataCollection feature has been reworked to improve the developer experience and make it more consistent with the rest of the API.

Unfortunately, this means that existing setups may need to be adjusted to work with the new API.

#### Upgrade guide

- The `DataCollections` class has been renamed to `DataCollection`. If you have used the `DataCollections` class in your code, you will need to update your code to use the new class name.

#### Changes

- Calling the `DataCollection` methods will no longer create the data collections directory automatically.
- The `DataCollection` class now validates the syntax of all data collection files during discovery, and throws a `ParseException` if the syntax is invalid.

#### Issues that may arise

If you start getting a `ParseException` when using the `DataCollection` class, it may be due to malformed data collection files.
Starting from this version, we validate the syntax of JSON and YAML in data files during discovery, including any front matter in Markdown data files.
We do this to help you catch errors early. See https://github.com/hydephp/develop/issues/1736 for more information.

For example, an empty or malformed JSON file will now throw an exception like this:

```php
\Hyde\Framework\Exceptions\ParseException: Invalid JSON in file: 'foo/baz.json' (Syntax error)
```

In order to normalize the thrown exceptions, we now rethrow the `ParseException` from `Symfony/Yaml` as our custom `ParseException` to match the JSON and Markdown validation.
Additionally, an exception will be thrown if a data file is empty, as this is unlikely to be intentional. Markdown files can have an empty body if front matter is present.
2 changes: 1 addition & 1 deletion _ide_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Features extends \Hyde\Facades\Features {}
class Config extends \Hyde\Facades\Config {}
/** @mixin \Illuminate\Filesystem\Filesystem */
class Filesystem extends \Hyde\Facades\Filesystem {}
class DataCollections extends \Hyde\Support\DataCollections {}
class DataCollection extends \Hyde\Support\DataCollection {}
class Includes extends \Hyde\Support\Includes {}
/** @mixin \Hyde\Foundation\Kernel\RouteCollection */
class Routes extends \Hyde\Foundation\Facades\Routes {}
Expand Down
2 changes: 1 addition & 1 deletion app/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
'MarkdownPage' => \Hyde\Pages\MarkdownPage::class,
'MarkdownPost' => \Hyde\Pages\MarkdownPost::class,
'DocumentationPage' => \Hyde\Pages\DocumentationPage::class,
'DataCollections' => \Hyde\Support\DataCollections::class,
'DataCollection' => \Hyde\Support\DataCollection::class,
'Includes' => \Hyde\Support\Includes::class,
'Feature' => \Hyde\Enums\Feature::class,
],
Expand Down
37 changes: 16 additions & 21 deletions docs/digging-deeper/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ navigation:

## Introduction to Hyde Data Collections

Hyde provides `DataCollections`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections.
Hyde provides `DataCollection`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections.
However, instead of accessing a database, it's all entirely file-based using static data files such as Markdown, Yaml, and JSON files which get
parsed into objects that you can easily work with.

Expand All @@ -24,15 +24,14 @@ In short, HydePHP finds files in the specified directory, turns each file into a
To make collections easy to use and understand, Hyde makes a few assumptions about the structure of your collections.
Follow these conventions and creating dynamic static sites will be a breeze.

1. Collections are accessed through static methods in the `DataCollections` class.
1. Collections are accessed through static methods in the `DataCollection` class.
2. Collections are stored as files in subdirectories of the `resources/collections` directory.
3. To get a collection, specify name of the subdirectory the files are stored in.
4. Data will be parsed into differing objects depending on which facade method you use. See the table below.
5. The class is aliased so that you can use it in Blade files without having to include the namespace.
6. While not enforced, each subdirectory should probably only have the same filetype to prevent developer confusion.
7. Unlike source files for pages, files starting with underscores are not ignored.
8. You can customize the source directory for collections through a service provider.
9. If the base source directory does not exist, it will be created for you.


## Available Collection Types
Expand All @@ -42,9 +41,9 @@ Follow these conventions and creating dynamic static sites will be a breeze.
The following facade methods for creating data collections are available:

```php
\Hyde\Support\DataCollections::markdown(string $name);
\Hyde\Support\DataCollections::yaml(string $name);
\Hyde\Support\DataCollections::json(string $name, bool $asArray = false);
\Hyde\Support\DataCollection::markdown(string $name);
\Hyde\Support\DataCollection::yaml(string $name);
\Hyde\Support\DataCollection::json(string $name, bool $asArray = false);
```

### Quick Reference Table
Expand All @@ -61,15 +60,15 @@ The following facade methods for creating data collections are available:
### Usage

```php
$collection = \Hyde\Support\DataCollections::markdown('name');
$collection = \Hyde\Support\DataCollection::markdown('name');
```

### Example returns

Here is an approximation of the data types contained by the variable created above:

```php
\Hyde\Support\DataCollections {
\Hyde\Support\DataCollection {
"testimonials/1.md" => Hyde\Markdown\Models\MarkdownDocument
"testimonials/2.md" => Hyde\Markdown\Models\MarkdownDocument
"testimonials/3.md" => Hyde\Markdown\Models\MarkdownDocument
Expand Down Expand Up @@ -108,15 +107,15 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit...
### Usage

```php
$collection = \Hyde\Support\DataCollections::yaml('name');
$collection = \Hyde\Support\DataCollection::yaml('name');
```

### Example returns

Here is an approximation of the data types contained by the variable created above:

```php
\Hyde\Support\DataCollections {
\Hyde\Support\DataCollection {
"authors/1.yaml" => Hyde\Markdown\Models\FrontMatter {
+data: array:1 [
"name" => "John Doe",
Expand All @@ -129,26 +128,22 @@ Here is an approximation of the data types contained by the variable created abo
Assuming the Yaml document looks like this:

```yaml
---
name: "John Doe"
email: "[email protected]"
```

>warning Note that the Yaml file should start with `---` to be parsed correctly.


## Json Collections

### Usage

```php
$collection = \Hyde\Support\DataCollections::json('name');
$collection = \Hyde\Support\DataCollection::json('name');
```

By default, the entries will be returned as `stdClass` objects. If you want to return an associative array instead, pass `true` as the second parameter:

```php
$collection = \Hyde\Support\DataCollections::json('name', true);
$collection = \Hyde\Support\DataCollection::json('name', true);
```

Since both return values use native PHP types, there are no example returns added here, as I'm sure you can imagine what they look like.
Expand Down Expand Up @@ -191,18 +186,18 @@ resources/collections

#### Using the Facade to Access the Collections

Now for the fun part! We will use the `DataCollections::markdown()` to access all our files into a convenient object.
Now for the fun part! We will use the `DataCollection::markdown()` to access all our files into a convenient object.
The class is registered with an alias, so you don't need to include any namespaces when in a Blade file.

The general syntax to use the facade is as follows:

```blade
DataCollections::markdown('subdirectory_name')
DataCollection::markdown('subdirectory_name')
```

This will return a Hyde DataCollections object, containing our Markdown files as MarkdownDocument objects. Here is a quick look at the object the facade returns:
This will return a Hyde DataCollection object, containing our Markdown files as MarkdownDocument objects. Here is a quick look at the object the facade returns:

<pre style="display: block; white-space: pre-wrap; padding: 1rem 1.5rem; overflow: initial !important; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 400 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; position: relative; z-index: 99999; word-break: break-all; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span class="sf-dump-default" style="display: inline; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; white-space: pre-wrap; position: relative; z-index: 99999; word-break: break-all;">^</span><span class="sf-dump-default" style="display: inline; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; white-space: pre-wrap; position: relative; z-index: 99999; word-break: break-all;"> </span><span style="display: inline; color: rgb(18, 153, 218);">Hyde\Support\DataCollections</span> {<span style="text-decoration: none; border: 0px; outline: none; color: rgb(160, 160, 160);">#651 <span style="display: inline;">▼</span></span><samp>
<pre style="display: block; white-space: pre-wrap; padding: 1rem 1.5rem; overflow: initial !important; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 400 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; position: relative; z-index: 99999; word-break: break-all; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span class="sf-dump-default" style="display: inline; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; white-space: pre-wrap; position: relative; z-index: 99999; word-break: break-all;">^</span><span class="sf-dump-default" style="display: inline; background-color: rgb(24, 23, 27); color: rgb(255, 132, 0); font: 12px Menlo, Monaco, Consolas, monospace; overflow-wrap: break-word; white-space: pre-wrap; position: relative; z-index: 99999; word-break: break-all;"> </span><span style="display: inline; color: rgb(18, 153, 218);">Hyde\Support\DataCollection</span> {<span style="text-decoration: none; border: 0px; outline: none; color: rgb(160, 160, 160);">#651 <span style="display: inline;">▼</span></span><samp>
#<span style="display: inline; color: rgb(255, 255, 255);">items</span>: <span style="display: inline; color: rgb(18, 153, 218);">array:3</span> [<span style="text-decoration: none; border: 0px; outline: none; color: rgb(160, 160, 160);"><span style="display: inline;">▼</span></span><samp>
"<span style="display: inline; color: rgb(86, 219, 58);">testimonials/1.md</span>" =&gt; <span style="display: inline; color: rgb(18, 153, 218);"><span>Hyde\Markdown\Models</span><span style="display: inline-block; text-overflow: ellipsis; max-width: none; white-space: nowrap; overflow: hidden; vertical-align: top; color: rgb(18, 153, 218);">\</span>MarkdownDocument</span> {<span style="text-decoration: none; border: 0px; outline: none; color: rgb(160, 160, 160);">#653 <span style="display: inline;">▼</span></span><samp>
+<span style="display: inline; color: rgb(255, 255, 255);">matter</span>: <span style="display: inline; color: rgb(18, 153, 218);"><span>Hyde\Markdown\Models</span><span style="display: inline-block; text-overflow: ellipsis; max-width: none; white-space: nowrap; overflow: hidden; vertical-align: top; color: rgb(18, 153, 218);">\</span>FrontMatter</span> {<span style="text-decoration: none; border: 0px; outline: none; color: rgb(160, 160, 160);">#652 <span style="display: inline;">▶</span></span>}
Expand All @@ -227,7 +222,7 @@ we are able to get the author from the front matter, and the content from the bo

```blade
// filepath _pages/testimonials.blade.php
@foreach(DataCollections::markdown('testimonials') as $testimonial)
@foreach(DataCollection::markdown('testimonials') as $testimonial)
<blockquote>
<p>{{ $testimonial->body }}</p>
<small>{{ $testimonial->matter['author'] }}</small>
Expand Down
10 changes: 5 additions & 5 deletions docs/digging-deeper/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ Note that these helpers targets those who write custom code and Blade templates,

## File-based Collections

Hyde provides `DataCollections`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. However, instead of accessing a database,
Hyde provides `DataCollection`, a subset of [Laravel Collections](https://laravel.com/docs/10.x/collections) giving you a similar developer experience to working with Eloquent Collections. However, instead of accessing a database,
it's all entirely file-based using static data files such as Markdown, Yaml, and JSON files which get parsed into objects that you can easily work with.

```php
use Hyde\Support\DataCollections;
use Hyde\Support\DataCollection;

// Gets all Markdown files in resources/collections/$name directory
DataCollections::markdown(string $name);
DataCollection::markdown(string $name);

// Gets all YAML files in resources/collections/$name directory
DataCollections::yaml(string $name);
DataCollection::yaml(string $name);

// Gets all JSON files in resources/collections/$name directory
DataCollections::json(string $name, bool $asArray = false);
DataCollection::json(string $name, bool $asArray = false);
```

See the [File-based Collections](collections) documentation for the full details.
Expand Down
50 changes: 50 additions & 0 deletions packages/framework/src/Framework/Exceptions/ParseException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Hyde\Framework\Exceptions;

use Throwable;
use RuntimeException;
use Illuminate\Support\Arr;

use function rtrim;
use function sprintf;
use function explode;

/** @experimental This class may change significantly before its release. */
class ParseException extends RuntimeException
{
/** @var int */
protected $code = 422;

public function __construct(string $file = '', ?Throwable $previous = null)
{
parent::__construct($this->formatMessage($file, $previous), previous: $previous);
}

protected function formatMessage(string $file, ?Throwable $previous): string
{
return rtrim(sprintf('Invalid %s in file%s %s', $this->getTypeLabel($file), $this->getFileLabel($file), $this->getContext($previous)));
}

protected function getTypeLabel(string $file): string
{
return match (Arr::last(explode('.', $file))) {
'md' => 'Markdown',
'yaml', 'yml' => 'Yaml',
'json' => 'Json',
default => 'data',
};
}

protected function getFileLabel(string $file): string
{
return $file ? sprintf(": '%s'", $file) : '';
}

protected function getContext(?Throwable $previous): string
{
return ($previous && $previous->getMessage()) ? sprintf('(%s)', rtrim($previous->getMessage(), '.')) : '';
}
}
Loading