From 0bf7b3a37622187b852aac38ec64b269bfca91eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 22 Apr 2018 12:47:12 +0200 Subject: [PATCH 1/4] Move test templates to a better place To avoid having to tweak project configuration to accept their syntax. --- .gitattributes | 1 + {tests/Formatter/templates => templates}/naive/person.html | 0 tests/Formatter/NaiveTemplateEngine.php | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) rename {tests/Formatter/templates => templates}/naive/person.html (100%) diff --git a/.gitattributes b/.gitattributes index 872b841b..c153ad6f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ +/templates export-ignore /tests export-ignore /.gitattributes export-ignore /.gitignore export-ignore diff --git a/tests/Formatter/templates/naive/person.html b/templates/naive/person.html similarity index 100% rename from tests/Formatter/templates/naive/person.html rename to templates/naive/person.html diff --git a/tests/Formatter/NaiveTemplateEngine.php b/tests/Formatter/NaiveTemplateEngine.php index fff5e125..4a1f75ee 100644 --- a/tests/Formatter/NaiveTemplateEngine.php +++ b/tests/Formatter/NaiveTemplateEngine.php @@ -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'; /** From ea49b3d2e096eaf476846bcbccd4b4ca357652fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 22 Apr 2018 13:01:34 +0200 Subject: [PATCH 2/4] Add attributes to custom formatter example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 82dd8135..c4c04bd2 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,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. From 7e10c7aa1b4d6c3291bd687fac384192004a524b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 22 Apr 2018 13:05:23 +0200 Subject: [PATCH 3/4] Add support for Plates as template engine --- README.md | 8 +++- composer.json | 2 + src/Formatter/Plates.php | 59 +++++++++++++++++++++++++ templates/plates/person.php | 9 ++++ tests/Formatter/PlatesTest.php | 79 ++++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 src/Formatter/Plates.php create mode 100644 templates/plates/person.php create mode 100644 tests/Formatter/PlatesTest.php diff --git a/README.md b/README.md index c4c04bd2..2a23a7c6 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,12 @@ 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)) If you want to create a customised formatter the only thing needed is to implement the `Formatter` interface: diff --git a/composer.json b/composer.json index 538ab779..4639fc8b 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "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", @@ -37,6 +38,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", "zendframework/zend-diactoros": "For concrete implementation of PSR-7" }, diff --git a/src/Formatter/Plates.php b/src/Formatter/Plates.php new file mode 100644 index 00000000..b2ad8f12 --- /dev/null +++ b/src/Formatter/Plates.php @@ -0,0 +1,59 @@ +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]); + } +} diff --git a/templates/plates/person.php b/templates/plates/person.php new file mode 100644 index 00000000..d494b12c --- /dev/null +++ b/templates/plates/person.php @@ -0,0 +1,9 @@ + +
+
+
Identifier
+
e($content->id)?>
+
Name
+
e($content->name)?>
+
+
diff --git a/tests/Formatter/PlatesTest.php b/tests/Formatter/PlatesTest.php new file mode 100644 index 00000000..c4867464 --- /dev/null +++ b/tests/Formatter/PlatesTest.php @@ -0,0 +1,79 @@ +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('
1
', $content); + self::assertContains('
Testing
', $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('
1
', $content); + self::assertContains('
Testing
', $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']); + } +} From 46b3702fc1c0c2e5930f415cab9d0668847031a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 22 Apr 2018 13:20:52 +0200 Subject: [PATCH 4/4] Add support for Twig as template engine --- README.md | 1 + composer.json | 2 + src/Formatter/Twig.php | 61 +++++++++++++++++++++++++++ templates/twig/person.twig | 8 ++++ tests/Formatter/TwigTest.php | 82 ++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 src/Formatter/Twig.php create mode 100644 templates/twig/person.twig create mode 100644 tests/Formatter/TwigTest.php diff --git a/README.md b/README.md index 2a23a7c6..e190c8da 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ We provide some basic formatters by default: * `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: diff --git a/composer.json b/composer.json index 4639fc8b..81084f8a 100644 --- a/composer.json +++ b/composer.json @@ -34,12 +34,14 @@ "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": { diff --git a/src/Formatter/Twig.php b/src/Formatter/Twig.php new file mode 100644 index 00000000..fb0a21d2 --- /dev/null +++ b/src/Formatter/Twig.php @@ -0,0 +1,61 @@ +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]); + } +} diff --git a/templates/twig/person.twig b/templates/twig/person.twig new file mode 100644 index 00000000..b6e7e5e7 --- /dev/null +++ b/templates/twig/person.twig @@ -0,0 +1,8 @@ +
+
+
Identifier
+
{{ content.id }}
+
Name
+
{{ content.name }}
+
+
diff --git a/tests/Formatter/TwigTest.php b/tests/Formatter/TwigTest.php new file mode 100644 index 00000000..ba8f7f2e --- /dev/null +++ b/tests/Formatter/TwigTest.php @@ -0,0 +1,82 @@ +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('
1
', $content); + self::assertContains('
Testing
', $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('
1
', $content); + self::assertContains('
Testing
', $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']); + } +}