Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.
Merged
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
51 changes: 38 additions & 13 deletions doc/book/template/interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,34 @@ namespace Zend\Expressive\Template;
interface TemplateInterface
{
/**
* Render a template, optionally with parameters.
*
* Implementations MUST support the `namespace::template` naming convention,
* and allow omitting the filename extension.
*
* @param string $name
* @param array|object $params
* @return string
*/
public function render($name, $params = []);

/**
* Add a template path to the engine.
*
* Adds a template path, with optional namespace the templates in that path
* provide.
*
* @param string $path
* @param string $namespace
*/
public function addPath($path, $namespace = null);

/**
* Add a template path to the engine.
*
* Adds a template path, with optional namespace the templates in that path
* provide.
*
* @return TemplatePath[]
*/
public function getPaths();
Expand All @@ -34,13 +49,19 @@ interface TemplateInterface
> Unfortunately, namespace syntax varies between different template engine
> implementations. As an example:
>
> - Plates uses the syntax `namespace::template`
> - Twig uses the syntax `@namespace/template`
> - zend-view does not natively support namespaces; we mimic it using normal
> directory syntax.
> - Plates uses the syntax `namespace::template`.
> - Twig uses the syntax `@namespace/template`.
> - zend-view does not natively support namespaces, though custom resolvers
> can provide the functionality.
>
> As such, it's not entirely possible to have engine-agnostic templates if you
> use namespaces.
> To make different engines compatible, we require implementations to support
> the syntax `namespace::template` (where `namespace::` is optional) when
> rendering. Additionally, we require that engines allow omitting the filename
> suffix.
>
> When using a `TemplateInterface` implementation, feel free to use namespaced
> templates, and to omit the filename suffix; this will make your code portable
> and allow it to use alternate template engines.


## Paths
Expand All @@ -53,10 +74,10 @@ the actual template. You may use the `addPath()` method to do so:
$template->addPath('templates');
```

Many template engines further allow *namespacing* templates; when adding a path,
you specify the template *namespace* that it fulfills, and the engine will only
return a template from that path if the namespace provided matches the namespace
for the path.
Template engines adapted for zend-expressive are also required to allow
*namespacing* templates; when adding a path, you specify the template
*namespace* that it fulfills, and the engine will only return a template from
that path if the namespace provided matches the namespace for the path.

```php
// Resolves to a path registered with the namespace "error";
Expand All @@ -81,9 +102,13 @@ of a template as the first argument:
$content = $template->render('foo');
```

One key reason to use templates, however, is to dynamically provide data to
inject in the template. You may do so by passing either an associative array or
an object as the second argument to `render()`:
You can specify a namespaced template using the syntax `namespace::template`;
the `template` segment of the template name may use additional directory
separators when necessary.

One key reason to use templates is to dynamically provide data to inject in the
template. You may do so by passing either an associative array or an object as
the second argument to `render()`:

```php
$content = $template->render('message', [
Expand Down
19 changes: 16 additions & 3 deletions doc/book/template/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ very specific to the project and/or organization.

We do, however, provide abstraction for templating via the interface
`Zend\Expressive\Template\TemplateInterface`, which allows you to write
middleware that is engine-agnostic. In this documentation, we'll detail the
features of this interface, the various implementations we provide, and how you
can configure, inject, and consume templating in your middleware.
middleware that is engine-agnostic. For Expressive, this means:

- All adapters MUST support template namespacing. Namespaces MUST be referenced
using the notation `namespace::template` when rendering.
- Adapters MUST allow rendering templates that omit the extension; they will, of
course, resolve to whatever default extension they require (or as configured).
- Adapters SHOULD allow passing an extension in the template name, but how that
is handled is left up to the adapter.
- Adapters SHOULD abstract layout capabilities. Many templating systems provide
this out of the box, or similar, compatible features such as template
inheritance. This should be transparent to end-users; they should be able to
simply render a template and assume it has the full content to return.

In this documentation, we'll detail the features of this interface, the various
implementations we provide, and how you can configure, inject, and consume
templating in your middleware.

We currently support:

Expand Down
20 changes: 20 additions & 0 deletions doc/book/template/zend-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ $zendView->setResolver($resolver);
$templates = new ZendView($zendView);
```

> ### Namespaced path resolving
>
> Expressive defines a custom zend-view resolver,
> `Zend\Expressive\Template\ZendView\NamespacedPathStackResolver`. This resolver
> provides the ability to segregate paths by namespace, and later resolve a
> template according to the namespace, using the `namespace::template` notation
> required of `TemplateInterface` implementations.
>
> The ZendView adapter ensures that:
>
> - An AggregateResolver is registered with the renderer. If the registered
> resolver is not an AggregateResolver, it creates one and adds the original
> resolver to it.
> - A NamespacedPathStackResolver is registered with the AggregateResolver, at
> a low priority (0), ensuring attempts to resolve hit it later.
>
> With resolvers such as the TemplateMapResolver, you can also resolve
> namespaced templates, mapping them directly to the template on the filesystem
> that matches; adding such a resolver can be a nice performance boost!

## Layouts

Unlike the other supported template engines, zend-view does not support layouts
Expand Down
2 changes: 2 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
<file>src</file>
<file>test</file>
<exclude-pattern>*/test/Template/TestAsset/plates-null.php</exclude-pattern>
<exclude-pattern>*/test/Template/TestAsset/test/test.js</exclude-pattern>
<exclude-pattern>*/test/Template/TestAsset/test/test.php</exclude-pattern>
</ruleset>
12 changes: 12 additions & 0 deletions src/Template/TemplateInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,31 @@
interface TemplateInterface
{
/**
* Render a template, optionally with parameters.
*
* Implementations MUST support the `namespace::template` naming convention,
* and allow omitting the filename extension.
*
* @param string $name
* @param array|object $params
* @return string
*/
public function render($name, $params = []);

/**
* Add a template path to the engine.
*
* Adds a template path, with optional namespace the templates in that path
* provide.
*
* @param string $path
* @param string $namespace
*/
public function addPath($path, $namespace = null);

/**
* Retrieve configured paths from the engine.
*
* @return TemplatePath[]
*/
public function getPaths();
Expand Down
28 changes: 27 additions & 1 deletion src/Template/Twig.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class Twig implements TemplateInterface
{
use ArrayParametersTrait;

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

/**
* @var TwigFilesystem
*/
Expand All @@ -35,7 +40,7 @@ class Twig implements TemplateInterface
*
* @param TwigEnvironment $template
*/
public function __construct(TwigEnvironment $template = null)
public function __construct(TwigEnvironment $template = null, $suffix = 'html')
{
if (null === $template) {
$template = $this->createTemplate($this->getDefaultLoader());
Expand All @@ -50,6 +55,7 @@ public function __construct(TwigEnvironment $template = null)

$this->template = $template;
$this->twigLoader = $loader;
$this->suffix = is_string($suffix) ? $suffix : 'html';
}

/**
Expand Down Expand Up @@ -82,6 +88,7 @@ private function getDefaultLoader()
*/
public function render($name, $params = [])
{
$name = $this->normalizeTemplate($name);
$params = $this->normalizeParams($params);
return $this->template->render($name, $params);
}
Expand Down Expand Up @@ -115,4 +122,23 @@ public function getPaths()
}
return $paths;
}

/**
* Normalize namespaced template.
*
* Normalizes templates in the format "namespace::template" to
* "@namespace/template".
*
* @param string $template
* @return string
*/
public function normalizeTemplate($template)
{
$template = preg_replace('#^([^:]+)::(.*)$#', '@$1/$2', $template);
if (! preg_match('#\.[a-z]+$#i', $template)) {
return sprintf('%s.%s', $template, $this->suffix);
}

return $template;
}
}
Loading