Skip to content

Commit

Permalink
Merge pull request #104 from StenopePHP/content_manager_interface
Browse files Browse the repository at this point in the history
Add a ContentManagerInterface
  • Loading branch information
ogizanagi authored Jul 15, 2021
2 parents 864a267 + d15ec44 commit 53696ed
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 59 deletions.
10 changes: 6 additions & 4 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Stenope\Bundle\Command\BuildCommand;
use Stenope\Bundle\Command\DebugCommand;
use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use Stenope\Bundle\Decoder\HtmlDecoder;
use Stenope\Bundle\Decoder\MarkdownDecoder;
use Stenope\Bundle\DependencyInjection\tags;
Expand Down Expand Up @@ -70,15 +71,16 @@
'$propertyAccessor' => service('property_accessor'),
'$expressionLanguage' => service(ExpressionLanguage::class)->nullOnInvalid(),
'$stopwatch' => service('debug.stopwatch')->nullOnInvalid(),
])
])->call('setContentManager', [service(ContentManagerInterface::class)])
->alias(ContentManagerInterface::class, ContentManager::class)

// Content providers factories
->set(ContentProviderFactory::class)->args(['$factories' => tagged_iterator(tags\content_provider_factory)])
->set(LocalFilesystemProviderFactory::class)->tag(tags\content_provider_factory)

// Debug
->set(DebugCommand::class)->args([
'$manager' => service(ContentManager::class),
'$manager' => service(ContentManagerInterface::class),
'$stopwatch' => service('stenope.build.stopwatch'),
])
->tag('console.command', ['command' => DebugCommand::getDefaultName()])
Expand Down Expand Up @@ -164,15 +166,15 @@

// Symfony HttpKernel controller argument resolver
->set(ContentArgumentResolver::class)
->args(['$contentManager' => service(ContentManager::class)])
->args(['$contentManager' => service(ContentManagerInterface::class)])
->tag('controller.argument_value_resolver', [
'priority' => 110, // Prior to RequestAttributeValueResolver to resolve from route attribute
])

// Twig
->set(ContentExtension::class)->tag('twig.extension')
->set(ContentRuntime::class)
->args(['$contentManager' => service(ContentManager::class)])
->args(['$contentManager' => service(ContentManagerInterface::class)])
->tag('twig.runtime')

// Assets
Expand Down
6 changes: 3 additions & 3 deletions doc/app/src/Controller/DocController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

use App\Model\Index;
use App\Model\Page;
use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class DocController extends AbstractController
{
private ContentManager $contentManager;
private ContentManagerInterface $contentManager;

public function __construct(ContentManager $contentManager)
public function __construct(ContentManagerInterface $contentManager)
{
$this->contentManager = $contentManager;
}
Expand Down
14 changes: 7 additions & 7 deletions doc/loading-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ In your controller (or service):
namespace App\Controller;
use App\Model\Article;
use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
Expand All @@ -80,7 +80,7 @@ class BlogController extends AbstractController
/**
* @Route("/", name="blog")
*/
public function index(ContentManager $contentManager)
public function index(ContentManagerInterface $contentManager)
{
return $this->render(
'blog/index.html.twig',
Expand All @@ -93,7 +93,7 @@ _Note: contents of the same type can very well be writen in different formats._

### Fetching a specific content

The ContentManager uses slugs to identify your content.
The content manager uses slugs to identify your content.

The `slug` argument must exactly match the static file name in your content directory.

Expand All @@ -105,7 +105,7 @@ Example: `$contentManager->getContent(Article::class, 'how-to-train-your-dragon'
namespace App\Controller;
use App\Model\Article;
use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
Expand All @@ -119,7 +119,7 @@ class BlogController extends AbstractController
/**
* @Route("/{slug}", name="article")
*/
public function article(ContentManager $contentManager, string $slug)
public function article(ContentManagerInterface $contentManager, string $slug)
{
return $this->render(
'blog/article.html.twig',
Expand Down Expand Up @@ -194,7 +194,7 @@ $myDrafts = $contentManager->getContents(
#### A custom callable supported by the PHP [usort](https://www.php.net/manual/fr/function.usort.php) function

```php
$tagedMobileArticles = $contentManager->getContents(
$taggedMobileArticles = $contentManager->getContents(
Article::class,
null,
fn (Article $article): bool => in_array('mobile', $article->tags)
Expand All @@ -206,7 +206,7 @@ $tagedMobileArticles = $contentManager->getContents(
```php
use function Stenope\Bundle\ExpressionLanguage\expr;
$tagedMobileArticles = $contentManager->getContents(
$taggedMobileArticles = $contentManager->getContents(
Article::class,
null,
expr('"mobile" in _.tags')
Expand Down
2 changes: 1 addition & 1 deletion doc/twig.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Twig integration

Stenope provides a Twig extension to help you interact with the `ContentManager`
Stenope provides a Twig extension to help you interact with the `ContentManagerInterface`
from your templates.

## Functions
Expand Down
4 changes: 2 additions & 2 deletions src/Behaviour/ContentManagerAwareInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

namespace Stenope\Bundle\Behaviour;

use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;

interface ContentManagerAwareInterface
{
/**
* Sets the owning ContentManager object.
*/
public function setContentManager(ContentManager $contentManager);
public function setContentManager(ContentManagerInterface $contentManager);
}
9 changes: 6 additions & 3 deletions src/Behaviour/ContentManagerAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@

namespace Stenope\Bundle\Behaviour;

use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;

/**
* @see ContentManagerAwareInterface
*/
trait ContentManagerAwareTrait
{
private ContentManager $contentManager;
private ContentManagerInterface $contentManager;

public function setContentManager(ContentManager $contentManager): void
public function setContentManager(ContentManagerInterface $contentManager): void
{
$this->contentManager = $contentManager;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Command/DebugCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Stenope\Bundle\Command;

use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use function Stenope\Bundle\ExpressionLanguage\expr;
use Stenope\Bundle\ExpressionLanguage\Expression;
use Stenope\Bundle\TableOfContent\Headline;
Expand All @@ -31,10 +31,10 @@ class DebugCommand extends Command

protected static $defaultName = 'debug:stenope:content';

private ContentManager $manager;
private ContentManagerInterface $manager;
private Stopwatch $stopwatch;

public function __construct(ContentManager $manager, Stopwatch $stopwatch)
public function __construct(ContentManagerInterface $manager, Stopwatch $stopwatch)
{
$this->manager = $manager;
$this->stopwatch = $stopwatch;
Expand Down
47 changes: 24 additions & 23 deletions src/ContentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Stopwatch\Stopwatch;

class ContentManager
class ContentManager implements ContentManagerInterface
{
private DecoderInterface $decoder;
private DenormalizerInterface $denormalizer;
Expand All @@ -51,6 +51,8 @@ class ContentManager

private bool $managerInjected = false;

private ?ContentManagerInterface $contentManager;

public function __construct(
DecoderInterface $decoder,
DenormalizerInterface $denormalizer,
Expand All @@ -76,17 +78,7 @@ public function __construct(
}

/**
* List all content for the given type
*
* @template T
*
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string|array|callable $sortBy String, array or callable
* @param string|array|callable|Expression $filterBy Array, callable or an {@link Expression} instance / string
* to filter out with an expression using the ExpressionLanguage
* component.
*
* @return array<string,T> List of decoded contents with their slug as key
* {@inheritdoc}
*/
public function getContents(string $type, $sortBy = null, $filterBy = null): array
{
Expand Down Expand Up @@ -120,14 +112,14 @@ public function getContents(string $type, $sortBy = null, $filterBy = null): arr
return $contents;
}

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

public function sortBy(array &$contents, $sortBy = null): void
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 {
Expand All @@ -141,14 +133,7 @@ public function sortBy(array &$contents, $sortBy = null): void
}

/**
* Fetch a specific content
*
* @template T
*
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string $id Unique identifier (slug)
*
* @return T An object of the given type.
* {@inheritdoc}
*/
public function getContent(string $type, string $id): object
{
Expand All @@ -171,6 +156,9 @@ public function getContent(string $type, string $id): object
throw new ContentNotFoundException($type, $id);
}

/**
* {@inheritdoc}
*/
public function reverseContent(Context $context): ?Content
{
$key = md5(serialize($context));
Expand Down Expand Up @@ -345,10 +333,23 @@ private function initProcessors(): void
if (!$this->managerInjected) {
foreach ($this->processors as $processor) {
if ($processor instanceof ContentManagerAwareInterface) {
$processor->setContentManager($this);
$processor->setContentManager($this->contentManager ?? $this);
}
}
$this->managerInjected = true;
}
}

/**
* Set the actual content manager instance to inject in processors.
* Useful whenever this content manager is decorated in order for the processor to use the decorating one.
*/
public function setContentManager(ContentManagerInterface $contentManager): void
{
if ($contentManager === $this) {
return;
}

$this->contentManager = $contentManager;
}
}
50 changes: 50 additions & 0 deletions src/ContentManagerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

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

namespace Stenope\Bundle;

use Stenope\Bundle\ReverseContent\Context;
use Symfony\Component\ExpressionLanguage\Expression;

interface ContentManagerInterface
{
/**
* List all content for the given type
*
* @template T
*
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string|array|callable $sortBy String, array or callable
* @param string|array|callable|Expression $filterBy Array, callable or an {@link Expression} instance / string
* to filter out with an expression using the ExpressionLanguage
* component.
*
* @return array<string,T> List of decoded contents with their slug as key
*/
public function getContents(string $type, $sortBy = null, $filterBy = null): array;

/**
* Fetch a specific content
*
* @template T
*
* @param class-string<T> $type Model FQCN e.g. "App/Model/Article"
* @param string $id Unique identifier (slug)
*
* @return T An object of the given type.
*/
public function getContent(string $type, string $id): object;

/**
* Attempt to reverse resolve a content according to a context.
* E.g: attempt to resolve a content relative to another one through its filesystem path.
*/
public function reverseContent(Context $context): ?Content;

public function supports(string $type): bool;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@

namespace Stenope\Bundle\HttpKernel\Controller\ArgumentResolver;

use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

class ContentArgumentResolver implements ArgumentValueResolverInterface
{
private ContentManager $contentManager;
private ContentManagerInterface $contentManager;

public function __construct(ContentManager $contentManager)
public function __construct(ContentManagerInterface $contentManager)
{
$this->contentManager = $contentManager;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ReverseContent/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

namespace Stenope\Bundle\ReverseContent;

use Stenope\Bundle\ContentManager;
use Stenope\Bundle\ContentManagerInterface;

/**
* Context from which to resolve a content.
*
* @see ContentManager::reverseContent()
* @see ContentManagerInterface::reverseContent()
*/
abstract class Context
{
Expand Down
Loading

0 comments on commit 53696ed

Please sign in to comment.