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

Refactor publication service and add tests for it #694

Merged
merged 81 commits into from
Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
662d334
Inline and remove method PublicationService::formatNameForStorage
caendesilva Nov 25, 2022
c596093
Test getPublicationTypes helper
caendesilva Nov 25, 2022
e9f2c35
Use Hyde::path helper instead of base_path for better compatibility
caendesilva Nov 25, 2022
0f2ab6e
Cleanup code
caendesilva Nov 25, 2022
bc1f5de
Extract method
caendesilva Nov 25, 2022
880a594
Change helper to void
caendesilva Nov 25, 2022
6958b5d
Add more tests
caendesilva Nov 25, 2022
cb3ea28
Cleanup code
caendesilva Nov 25, 2022
6e3c1bd
Add assertion
caendesilva Nov 25, 2022
cf6d028
Split out assertion to new test method
caendesilva Nov 25, 2022
289aa7e
Split comma-separated values into multiple lines
caendesilva Nov 25, 2022
3d8ed2f
Use strong types for sorting callback
caendesilva Nov 25, 2022
6af3175
Convert closure to arrow function
caendesilva Nov 25, 2022
f52431d
Union type int
caendesilva Nov 25, 2022
c95dad9
Use the sortDirection schema option
caendesilva Nov 25, 2022
b71630d
Test sorting
caendesilva Nov 25, 2022
5a2a8cf
Extract method
caendesilva Nov 25, 2022
aad1197
Remove collection sorting to reduce complexity
caendesilva Nov 25, 2022
5a418de
Rename method getPublicationData to parsePublicationFile
caendesilva Nov 25, 2022
cee9ba1
Replace qualifier with an import
caendesilva Nov 25, 2022
bf9a641
Add parameter example
caendesilva Nov 25, 2022
eecd88d
Read file using absolute path
caendesilva Nov 25, 2022
8946c47
Remove unused use statement
caendesilva Nov 25, 2022
74a7b74
Apply fixes from StyleCI
StyleCIBot Nov 25, 2022
8b97f66
Remove redundant __slug magic front matter property
caendesilva Nov 25, 2022
b6cd1c7
Merge branch 'refactor-PublicationService' of github.com:hydephp/deve…
caendesilva Nov 25, 2022
995193a
Pass along relative path
caendesilva Nov 25, 2022
928dbbb
Test publicationTypeExists method
caendesilva Nov 25, 2022
4d71e7d
Simplify publicationTypeExists by inlining conditional
caendesilva Nov 25, 2022
f8fe0e1
Remove redundant PHPDoc
caendesilva Nov 25, 2022
0caece6
Remove __createdDatetime magic front matter property
caendesilva Nov 25, 2022
9f01ff0
Inline local variables
caendesilva Nov 25, 2022
fb42f10
Unwrap unnecessary basename function call
caendesilva Nov 25, 2022
2031d2c
Add name identifiers to arguments
caendesilva Nov 25, 2022
eaf130e
Revert "Remove redundant PHPDoc"
caendesilva Nov 25, 2022
d026d26
Extract helper
caendesilva Nov 25, 2022
b6f5f7c
Remove throws tags not thrown within method body
caendesilva Nov 25, 2022
ded4bdd
Remove generic throws tags without documentation
caendesilva Nov 25, 2022
fc84c05
Refactor foreach loop with collection mapper
caendesilva Nov 25, 2022
7f81080
Type return value
caendesilva Nov 25, 2022
b5afc58
Remove unused glob flag
caendesilva Nov 25, 2022
db9ca61
Add todo
caendesilva Nov 25, 2022
8291301
Add fixme
caendesilva Nov 25, 2022
84e888a
Extract helper
caendesilva Nov 25, 2022
5b181b5
Convert string interpolation to concatenation and inline variable
caendesilva Nov 25, 2022
2a05f97
Respect custom content roots
caendesilva Nov 25, 2022
e63eb10
Add todo
caendesilva Nov 25, 2022
073f065
Apply fixes from StyleCI
StyleCIBot Nov 25, 2022
be24703
Update PHPDoc comment
caendesilva Nov 25, 2022
f28ffdb
Rename internal parameter
caendesilva Nov 25, 2022
bc39c20
Refactor parsePublicationFile method to use identifier to match pages
caendesilva Nov 25, 2022
8adf63e
Normalize identifier to allow included extension
caendesilva Nov 25, 2022
2c72498
Use basename for identifier that is concatenated in its constructor
caendesilva Nov 25, 2022
4c10c00
Test parsePublicationFile method
caendesilva Nov 25, 2022
e47d5ea
Add parsing error tests
caendesilva Nov 25, 2022
8606ebe
Add throws tag
caendesilva Nov 25, 2022
9292078
Apply fixes from StyleCI
StyleCIBot Nov 25, 2022
aa0fd5a
Finish PHPDoc with period for style
caendesilva Nov 25, 2022
86087b9
Use absolute path when interacting with the filesystem
caendesilva Nov 25, 2022
785ec78
Remove premature file path qualifier
caendesilva Nov 25, 2022
ee1d689
Remove premature file path qualifier
caendesilva Nov 25, 2022
aeced2b
Use relative paths for method arguments
caendesilva Nov 25, 2022
6747c14
Import functions
caendesilva Nov 25, 2022
df81966
Convert paths to relative within the glob helper
caendesilva Nov 25, 2022
086706c
Revert "Convert paths to relative within the glob helper"
caendesilva Nov 25, 2022
cf31ef7
Apply fixes from StyleCI
StyleCIBot Nov 25, 2022
2673ab0
Remove todo as it's handled by global media paths
caendesilva Nov 25, 2022
7747ac1
Add _media prefix within helper so it normalizes directory separators
caendesilva Nov 25, 2022
67ae451
Test getMediaForPubType helper
caendesilva Nov 25, 2022
6c55b14
Paths must be stored relatively internally
caendesilva Nov 25, 2022
f74a9cb
Refactor foreach loops to use collection mappers
caendesilva Nov 25, 2022
1a088fc
Refactor to pass data to path helper
caendesilva Nov 25, 2022
42fb16f
Convert string interpolation to concatenation and inline local variable
caendesilva Nov 25, 2022
b4bcfad
Replace local variables with helper methods
caendesilva Nov 25, 2022
b4ae7a9
Apply fixes from StyleCI
StyleCIBot Nov 25, 2022
8cfb492
Update PHPDoc to match simplified method
caendesilva Nov 25, 2022
0b465a5
Shorten helper method name
caendesilva Nov 25, 2022
4f79e1b
Shift where ->getDirectory() is called
caendesilva Nov 25, 2022
6a7befd
Convert concatenation to a scalar value
caendesilva Nov 25, 2022
5dc598e
Introduce parameter
caendesilva Nov 25, 2022
6fbfbaf
Replace self with static
caendesilva Nov 25, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Hyde\Console\Commands\Interfaces\CommandHandleInterface;
use Hyde\Console\Concerns\ValidatingCommand;
use Hyde\Framework\Actions\CreatesNewPublicationType;
use Hyde\Framework\Features\Publications\PublicationService;
use Illuminate\Support\Str;
use InvalidArgumentException;
use LaravelZero\Framework\Commands\Command;
use Rgasch\Collection\Collection;
Expand All @@ -35,7 +35,7 @@ public function handle(): int
$title = $this->argument('title');
if (! $title) {
$title = trim($this->askWithValidation('name', 'Publication type name', ['required', 'string']));
$dirname = PublicationService::formatNameForStorage($title);
$dirname = Str::slug($title);
if (file_exists($dirname) && is_dir($dirname) && count(scandir($dirname)) > 2) {
throw new InvalidArgumentException("Storage path [$dirname] already exists");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class PublicationType implements JsonSerializable, Jsonable, Arrayable

public static function get(string $name): static
{
return static::fromFile(Hyde::path("$name/schema.json"));
return static::fromFile("$name/schema.json");
}

public static function fromFile(string $schemaFile): static
Expand Down Expand Up @@ -126,7 +126,7 @@ public function save(?string $path = null): void

protected static function parseSchemaFile(string $schemaFile): array
{
return json_decode(file_get_contents($schemaFile), true, 512, JSON_THROW_ON_ERROR);
return json_decode(file_get_contents(Hyde::path($schemaFile)), true, 512, JSON_THROW_ON_ERROR);
}

protected static function getRelativeDirectoryName(string $schemaFile): array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace Hyde\Framework\Features\Publications;

use Carbon\Carbon;
use function basename;
use function dirname;
use Exception;
use function glob;
use Hyde\Framework\Features\Publications\Models\PublicationType;
use Hyde\Hyde;
use Hyde\Pages\PublicationPage;
Expand All @@ -18,117 +21,96 @@
*/
class PublicationService
{
/**
* Format the publication type name to a suitable representation for file storage.
*/
public static function formatNameForStorage(string $pubTypeNameRaw): string
{
return Str::slug($pubTypeNameRaw);
}

/**
* Return a collection of all defined publication types, indexed by the directory name.
*
* @todo We might want to refactor to cache this in the Kernel, maybe under $publications?
*
* @return Collection<string, PublicationType>
*
* @throws \Exception
*/
public static function getPublicationTypes(): Collection
{
$root = Hyde::path();
$schemaFiles = glob("$root/*/schema.json", GLOB_BRACE);
return Collection::create(static::getSchemaFiles())->mapWithKeys(function (string $schemaFile): array {
$publicationType = PublicationType::fromFile(Hyde::pathToRelative($schemaFile));

$pubTypes = Collection::create();
foreach ($schemaFiles as $schemaFile) {
$publicationType = PublicationType::fromFile($schemaFile);
$pubTypes->{$publicationType->getDirectory()} = $publicationType;
}

return $pubTypes;
return [$publicationType->getDirectory() => $publicationType];
});
}

/**
* Return all publications for a given pub type, optionally sorted by the publication's sortField.
*
* @throws \Safe\Exceptions\FilesystemException
* Return all publications for a given publication type.
*/
public static function getPublicationsForPubType(PublicationType $pubType, $sort = true): Collection
public static function getPublicationsForPubType(PublicationType $pubType): Collection
{
$root = base_path();
$files = glob("$root/{$pubType->getDirectory()}/*.md");

$publications = Collection::create();
foreach ($files as $file) {
$publications->add(self::getPublicationData($file));
}

if ($sort) {
return $publications->sortBy(function ($publication) use ($pubType) {
return $publication->matter->{$pubType->sortField};
});
}

return $publications;
return Collection::create(static::getPublicationFiles($pubType->getDirectory()))->map(function (string $file): PublicationPage {
return static::parsePublicationFile(Hyde::pathToRelative($file));
});
}

/**
* Return all media items for a given publication type.
*/
public static function getMediaForPubType(PublicationType $pubType, $sort = true): Collection
public static function getMediaForPubType(PublicationType $pubType): Collection
{
$root = Hyde::path();
$files = glob("$root/_media/{$pubType->getDirectory()}/*.{jpg,jpeg,png,gif,pdf}", GLOB_BRACE);

$media = Collection::create();
foreach ($files as $file) {
$media->add($file);
}

if ($sort) {
return $media->sort()->values();
}

return $media;
return Collection::create(static::getMediaFiles($pubType->getDirectory()))->map(function (string $file): string {
return Hyde::pathToRelative($file);
});
}

/**
* Read an MD file and return the parsed data.
* Parse a publication Markdown source file and return a PublicationPage object.
*
* @throws \Safe\Exceptions\FilesystemException
* @param string $identifier Example: my-publication/hello.md or my-publication/hello
*/
public static function getPublicationData(string $mdFileName): PublicationPage
public static function parsePublicationFile(string $identifier): PublicationPage
{
$fileData = file_get_contents($mdFileName);
if (! $fileData) {
throw new \Exception("No data read from [$mdFileName]");
}
$identifier = Str::replaceLast('.md', '', $identifier);
$fileData = static::getFileData("$identifier.md");

$parsedFileData = YamlFrontMatter::markdownCompatibleParse($fileData);
$matter = $parsedFileData->matter();
$markdown = $parsedFileData->body();
$matter['__slug'] = basename($mdFileName, '.md');
$matter['__createdDatetime'] = Carbon::createFromTimestamp($matter['__createdAt']);

$type = PublicationType::get(basename(dirname($mdFileName)));

$identifier = basename($mdFileName, '.md');

return new PublicationPage($type, $identifier, $matter, $markdown);
return new PublicationPage(
type: PublicationType::get(dirname($identifier)),
identifier: basename($identifier),
matter: $parsedFileData->matter(),
markdown: $parsedFileData->body()
);
}

/**
* Check whether a given publication type exists.
*
* @throws \Exception
*/
public static function publicationTypeExists(string $pubTypeName, bool $isRaw = true): bool
public static function publicationTypeExists(string $pubTypeName): bool
{
return static::getPublicationTypes()->has(Str::slug($pubTypeName));
}

/**
* @throws \Safe\Exceptions\FilesystemException
* @throws \Exception If the file could not be read.
*/
protected static function getFileData(string $filepath): string
{
if ($isRaw) {
$pubTypeName = self::formatNameForStorage($pubTypeName);
$fileData = file_get_contents(Hyde::path($filepath));
if (! $fileData) {
throw new Exception("No data read from [$filepath]");
}

return self::getPublicationTypes()->has($pubTypeName);
return $fileData;
}

protected static function getSchemaFiles(): array
{
return glob(Hyde::path(Hyde::getSourceRoot()).'/*/schema.json');
}

protected static function getPublicationFiles(string $directory): array
{
return glob(Hyde::path("$directory/*.md"));
}

protected static function getMediaFiles(string $directory, string $extensions = '{jpg,jpeg,png,gif,pdf}'): array
{
return glob(Hyde::path("_media/$directory/*.$extensions"), GLOB_BRACE);
}
}
4 changes: 2 additions & 2 deletions packages/framework/tests/Feature/PublicationTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public function test_can_load_from_json_file()
'directory' => 'tests/fixtures',
]));

$this->assertEquals($publicationType, PublicationType::fromFile(Hyde::path('tests/fixtures/test-publication-schema.json')));
$this->assertEquals($publicationType, PublicationType::fromFile(('tests/fixtures/test-publication-schema.json')));
}

public function test_get_fields_method_returns_collection_of_field_objects()
Expand All @@ -118,7 +118,7 @@ public function test_get_method_can_find_existing_file_on_disk()
public function test_get_method_fails_if_publication_type_does_not_exist()
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Could not parse schema file '.Hyde::path('missing/schema.json'));
$this->expectExceptionMessage('Could not parse schema file '.('missing/schema.json'));
PublicationType::get('missing');
}

Expand Down
Loading