Skip to content

Commit

Permalink
Merge pull request #12 from lcobucci/add-template-engines
Browse files Browse the repository at this point in the history
Add template engines
  • Loading branch information
lcobucci authored Apr 22, 2018
2 parents 75b55fe + 46b3702 commit 7c26b21
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/templates export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,13 @@ final class MyHandler implements RequestHandlerInterface

### Formatters

We provide some basic formatters by default: `Json`, `StringCast`, and
`JmsSerializer` (this one requires you to also install `jms/serializer`, sure).
We provide some basic formatters by default:

* `Json`
* `StringCast`
* `JmsSerializer` (requires you to also install and configure [`jms/serializer`](https://jmsyst.com/libs/serializer))
* `Plates` (requires you to also install and configure [`league/plates`](http://platesphp.com))
* `Twig` (requires you to also install and configure [`twig/twig`](https://twig.symfony.com))

If you want to create a customised formatter the only thing needed is to
implement the `Formatter` interface:
Expand All @@ -163,7 +168,7 @@ use Lcobucci\ContentNegotiation\Formatter;

final class MyFancyFormatter implements Formatter
{
public function format($content): string
public function format($content, array $attributes = []): string
{
// Performs all the magic with $content and creates $result with a
// `string` containing the formatted data.
Expand Down
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,21 @@
"doctrine/coding-standard": "^4.0",
"infection/infection": "^0.8",
"jms/serializer": "^1.11",
"league/plates": "^3.3",
"middlewares/negotiation": "^1.0",
"phpstan/phpstan": "^0.10@dev",
"phpstan/phpstan-phpunit": "^0.10@dev",
"phpstan/phpstan-strict-rules": "^0.10@dev",
"phpunit/phpunit": "^7.0",
"squizlabs/php_codesniffer": "^3.2",
"twig/twig": "^2.0",
"zendframework/zend-diactoros": "^1.7"
},
"suggest": {
"jms/serializer": "For content formatting using a more flexible serializer",
"league/plates": "For content formatting using Plates as template engine",
"middlewares/negotiation": "For acceptable format identification",
"twig/twig": "For content formatting using Twig as template engine",
"zendframework/zend-diactoros": "For concrete implementation of PSR-7"
},
"autoload": {
Expand Down
59 changes: 59 additions & 0 deletions src/Formatter/Plates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);

namespace Lcobucci\ContentNegotiation\Formatter;

use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted;
use Lcobucci\ContentNegotiation\Formatter;
use League\Plates\Engine;
use Throwable;

final class Plates implements Formatter
{
private const DEFAULT_ATTRIBUTE = 'template';

/**
* @var Engine
*/
private $engine;

/**
* @var string
*/
private $attributeName;

public function __construct(Engine $engine, string $attributeName = self::DEFAULT_ATTRIBUTE)
{
$this->engine = $engine;
$this->attributeName = $attributeName;
}

/**
* {@inheritdoc}
*/
public function format($content, array $attributes = []): string
{
try {
return $this->render($content, $attributes);
} catch (Throwable $exception) {
throw new ContentCouldNotBeFormatted(
'An error occurred while formatting using plates',
$exception->getCode(),
$exception
);
}
}

/**
* @param mixed $content
* @param mixed[] $attributes
*
* @throws Throwable
*/
private function render($content, array $attributes = []): string
{
$template = $attributes[$this->attributeName] ?? '';

return $this->engine->render($template, ['content' => $content]);
}
}
61 changes: 61 additions & 0 deletions src/Formatter/Twig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);

namespace Lcobucci\ContentNegotiation\Formatter;

use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted;
use Lcobucci\ContentNegotiation\Formatter;
use Throwable;
use Twig_Environment;

final class Twig implements Formatter
{
private const DEFAULT_ATTRIBUTE = 'template';

/**
* @var Twig_Environment
*/
private $environment;

/**
* @var string
*/
private $attributeName;

public function __construct(
Twig_Environment $environment,
string $attributeName = self::DEFAULT_ATTRIBUTE
) {
$this->environment = $environment;
$this->attributeName = $attributeName;
}

/**
* {@inheritdoc}
*/
public function format($content, array $attributes = []): string
{
try {
return $this->render($content, $attributes);
} catch (Throwable $exception) {
throw new ContentCouldNotBeFormatted(
'An error occurred while formatting using twig',
$exception->getCode(),
$exception
);
}
}

/**
* @param mixed $content
* @param mixed[] $attributes
*
* @throws Throwable
*/
private function render($content, array $attributes = []): string
{
$template = $attributes[$this->attributeName] ?? '';

return $this->environment->render($template, ['content' => $content]);
}
}
File renamed without changes.
9 changes: 9 additions & 0 deletions templates/plates/person.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php assert($content instanceof \Lcobucci\ContentNegotiation\Tests\PersonDto) ?>
<section>
<dl>
<dt>Identifier</dt>
<dd><?=$this->e($content->id)?></dd>
<dt>Name</dt>
<dd><?=$this->e($content->name)?></dd>
</dl>
</section>
8 changes: 8 additions & 0 deletions templates/twig/person.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<section>
<dl>
<dt>Identifier</dt>
<dd>{{ content.id }}</dd>
<dt>Name</dt>
<dd>{{ content.name }}</dd>
</dl>
</section>
2 changes: 1 addition & 1 deletion tests/Formatter/NaiveTemplateEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

final class NaiveTemplateEngine implements Formatter
{
private const BASE_DIR = __DIR__ . '/templates/naive/';
private const BASE_DIR = __DIR__ . '/../../templates/naive/';
private const EXTENSION = 'html';

/**
Expand Down
79 changes: 79 additions & 0 deletions tests/Formatter/PlatesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);

namespace Lcobucci\ContentNegotiation\Tests\Formatter;

use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted;
use Lcobucci\ContentNegotiation\Formatter\Plates;
use Lcobucci\ContentNegotiation\Tests\PersonDto;
use League\Plates\Engine;
use PHPUnit\Framework\TestCase;
use function dirname;

/**
* @coversDefaultClass \Lcobucci\ContentNegotiation\Formatter\Plates
*/
final class PlatesTest extends TestCase
{
/**
* @var Engine
*/
private $engine;

/**
* @before
*/
public function configureEngine(): void
{
$this->engine = new Engine(dirname(__DIR__, 2) . '/templates/plates');
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldReturnContentFormattedByPlates(): void
{
$formatter = new Plates($this->engine);
$content = $formatter->format(new PersonDto(1, 'Testing'), ['template' => 'person']);

self::assertContains('<dd>1</dd>', $content);
self::assertContains('<dd>Testing</dd>', $content);
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldReadTemplateNameFromCustomAttribute(): void
{
$formatter = new Plates($this->engine, 'fancy!');
$content = $formatter->format(new PersonDto(1, 'Testing'), ['fancy!' => 'person']);

self::assertContains('<dd>1</dd>', $content);
self::assertContains('<dd>Testing</dd>', $content);
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldConvertAnyPlatesException(): void
{
$formatter = new Plates($this->engine);

$this->expectException(ContentCouldNotBeFormatted::class);
$this->expectExceptionMessage('An error occurred while formatting using plates');

$formatter->format(new PersonDto(1, 'Testing'), ['template' => 'no-template-at-all']);
}
}
82 changes: 82 additions & 0 deletions tests/Formatter/TwigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);

namespace Lcobucci\ContentNegotiation\Tests\Formatter;

use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted;
use Lcobucci\ContentNegotiation\Formatter\Twig;
use Lcobucci\ContentNegotiation\Tests\PersonDto;
use PHPUnit\Framework\TestCase;
use Twig_Environment;
use Twig_Loader_Filesystem;
use function dirname;

/**
* @coversDefaultClass \Lcobucci\ContentNegotiation\Formatter\Twig
*/
final class TwigTest extends TestCase
{
/**
* @var Twig_Environment
*/
private $environment;

/**
* @before
*/
public function configureEngine(): void
{
$this->environment = new Twig_Environment(
new Twig_Loader_Filesystem('templates/twig', dirname(__DIR__, 2) . '/')
);
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldReturnContentFormattedByPlates(): void
{
$formatter = new Twig($this->environment);
$content = $formatter->format(new PersonDto(1, 'Testing'), ['template' => 'person.twig']);

self::assertContains('<dd>1</dd>', $content);
self::assertContains('<dd>Testing</dd>', $content);
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldReadTemplateNameFromCustomAttribute(): void
{
$formatter = new Twig($this->environment, 'fancy!');
$content = $formatter->format(new PersonDto(1, 'Testing'), ['fancy!' => 'person.twig']);

self::assertContains('<dd>1</dd>', $content);
self::assertContains('<dd>Testing</dd>', $content);
}

/**
* @test
*
* @covers ::__construct()
* @covers ::format()
* @covers ::render()
*/
public function formatShouldConvertAnyTwigException(): void
{
$formatter = new Twig($this->environment);

$this->expectException(ContentCouldNotBeFormatted::class);
$this->expectExceptionMessage('An error occurred while formatting using twig');

$formatter->format(new PersonDto(1, 'Testing'), ['template' => 'no-template-at-all']);
}
}

0 comments on commit 7c26b21

Please sign in to comment.