Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion src/Content.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ final class Content
private ?\DateTimeImmutable $lastModified;
private ?\DateTimeImmutable $createdAt;
private array $metadata;
private bool $list;

public function __construct(
string $slug,
Expand All @@ -25,7 +26,8 @@ public function __construct(
string $format,
?\DateTimeImmutable $lastModified = null,
?\DateTimeImmutable $createdAt = null,
array $metadata = []
array $metadata = [],
bool $list = false,
) {
$this->slug = $slug;
$this->type = $type;
Expand All @@ -34,6 +36,7 @@ public function __construct(
$this->lastModified = $lastModified;
$this->createdAt = $createdAt;
$this->metadata = $metadata;
$this->list = $list;
}

public function getSlug(): string
Expand Down Expand Up @@ -70,4 +73,9 @@ public function getMetadata(): array
{
return $this->metadata;
}

public function isList(): bool
{
return $this->list;
}
}
91 changes: 91 additions & 0 deletions src/ContentBag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

/*
* This file is part of the "StenopePHP/Stenope" bundle.
*
* @author Thomas Jarrand <thomas.jarrand@gmail.com>
*/

namespace Stenope\Bundle;

use Stenope\Bundle\Exception\RuntimeException;

class ContentBag
{
private array $contents = [];
private bool $locked = false;
private string $type;

/** @var callable Sorting function */
private $sorter = null;

/** @var callable Filter function */
private $filter = null;

public function __construct(string $type, ?callable $sorter = null, ?callable $filter = null)
{
$this->type = $type;
$this->sorter = $sorter;
$this->filter = $filter;
}

public function add(string $identifier, object $object): void
{
if ($this->locked) {
throw new RuntimeException('Contents have already been filtered and sorted.', 1);
}

if (isset($this->contents[$identifier])) {
throw new RuntimeException(sprintf(
'Found multiple contents of type "%s" with the same "%s" identifier.',
$this->type,
$identifier
));
}

$this->contents[$identifier] = $object;
}

public function getContents(): array
{
try {
$this->applyFilter();
} catch (\Throwable $exception) {
throw new RuntimeException(sprintf('There was a problem filtering %s.', $this->type), 0, $exception);
}

try {
$this->applySort();
} catch (\Throwable $exception) {
throw new RuntimeException(sprintf('There was a problem sorting %s.', $this->type), 0, $exception);
}

$this->locked = true;

return $this->contents;
}

private function applyFilter(): void
{
if ($this->filter === null) {
return;
}

$this->contents = array_filter($this->contents, $this->filter);
}

private function applySort(): void
{
if ($this->sorter === null) {
return;
}

set_error_handler(static function (int $severity, string $message, ?string $file, ?int $line): void {
throw new \ErrorException($message, $severity, $severity, $file, $line);
});

uasort($this->contents, $this->sorter);

restore_error_handler();
}
}
61 changes: 18 additions & 43 deletions src/ContentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,60 +82,27 @@ public function __construct(
*/
public function getContents(string $type, $sortBy = null, $filterBy = null): array
{
$contents = [];
$contents = new ContentBag($type, $this->getSortFunction($sortBy), $this->getFilterFunction($filterBy));

foreach ($this->getProviders($type) as $provider) {
foreach ($provider->listContents() as $content) {
if (isset($contents[$content->getSlug()])) {
throw new RuntimeException(sprintf(
'Found multiple contents of type "%s" with the same "%s" identifier.',
$content->getType(),
$content->getSlug()
));
if ($content->isList()) {
foreach ($this->load($content) as $index => $object) {
$contents->add("{$content->getSlug()}[{$index}]", $object);
}
} else {
$contents->add($content->getSlug(), $this->load($content));
}
$contents[$content->getSlug()] = $this->load($content);
}
}

try {
$this->filterBy($contents, $filterBy);
} catch (\Throwable $exception) {
throw new RuntimeException(sprintf('There was a problem filtering %s.', $type), 0, $exception);
}

try {
$this->sortBy($contents, $sortBy);
} catch (\Throwable $exception) {
throw new RuntimeException(sprintf('There was a problem sorting %s.', $type), 0, $exception);
}

return $contents;
}

private function filterBy(array &$contents, $filterBy = null): void
{
if ($filter = $this->getFilterFunction($filterBy)) {
$contents = array_filter($contents, $filter);
}
}

private function sortBy(array &$contents, $sortBy = null): void
{
if ($sorter = $this->getSortFunction($sortBy)) {
set_error_handler(static function (int $severity, string $message, ?string $file, ?int $line): void {
throw new \ErrorException($message, $severity, $severity, $file, $line);
});

uasort($contents, $sorter);

restore_error_handler();
}
return $contents->getContents();
}

/**
* {@inheritdoc}
*/
public function getContent(string $type, string $id): object
public function getContent(string $type, string $id, ?int $index = null): object
{
if ($this->stopwatch) {
$event = $this->stopwatch->start('get_content', 'stenope');
Expand All @@ -149,6 +116,14 @@ public function getContent(string $type, string $id): object
$event->stop();
}

if ($content->isList()) {
if ($index === null) {
throw new RuntimeException("No index provided for list content \"$id\".", 1);
}

return $loaded[$index];
}

return $loaded;
}
}
Expand Down Expand Up @@ -228,7 +203,7 @@ private function load(Content $content)

$this->crawlers->saveAll($content, $data);

$data = $this->denormalizer->denormalize($data, $content->getType(), $content->getFormat(), [
$data = $this->denormalizer->denormalize($data, $content->getType() . ($content->isList() ? '[]' : ''), $content->getFormat(), [
SkippingInstantiatedObjectDenormalizer::SKIP => true,
]);

Expand Down
7 changes: 4 additions & 3 deletions src/ContentManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ public function getContents(string $type, $sortBy = null, $filterBy = null): arr
*
* @template T of object
*
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string $id Unique identifier (slug)
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string $id Unique identifier (slug)
* @param int $index Index (for content lists)
*
* @return T An object of the given type.
*/
public function getContent(string $type, string $id): object;
public function getContent(string $type, string $id, ?int $index = null): object;

/**
* Attempt to reverse resolve a content according to a context.
Expand Down
3 changes: 2 additions & 1 deletion src/Provider/Factory/LocalFilesystemProviderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ class LocalFilesystemProviderFactory implements ContentProviderFactoryInterface
public function create(string $type, array $config): ContentProviderInterface
{
return new LocalFilesystemProvider(
$config['class'],
trim($config['class'], '[]'),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit anecdotic for the use-case but still: the second arg of trim is a list of chars to remove, not a string to remove.
Something like trimSuffix should be used instead: https://symfony.com/doc/current/components/string.html#methods-to-pad-and-trim

$config['path'],
$config['depth'] ?? null,
$config['excludes'] ?? [],
$config['patterns'] ?? ['*'],
str_ends_with($config['class'], '[]')
);
}

Expand Down
8 changes: 6 additions & 2 deletions src/Provider/LocalFilesystemProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class LocalFilesystemProvider implements ReversibleContentProviderInterface
private string $path;
private ?string $depth;
private array $excludes;
private bool $list;

/** @var string[] */
private array $patterns;
Expand All @@ -30,13 +31,15 @@ public function __construct(
string $path,
?string $depth = null,
array $excludes = [],
array $patterns = ['*']
array $patterns = ['*'],
bool $list = false,
) {
$this->supportedClass = $supportedClass;
$this->path = $path;
$this->depth = $depth;
$this->excludes = $excludes;
$this->patterns = $patterns;
$this->list = $list;
}

/**
Expand Down Expand Up @@ -106,7 +109,8 @@ private function fromFile(\SplFileInfo $file): Content
[
'path' => $file->getRealPath(),
'provider' => LocalFilesystemProviderFactory::TYPE,
]
],
$this->list,
);
}

Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Provider/LocalFilesystemProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public function testGetContent(): void
"path" => "$basePath/content/foo/foo.md",
"provider" => "files",
]
-list: false
}
DUMP,
$provider->getContent('foo'),
Expand Down