Skip to content

marko/view-twig

Twig templating driver for the Marko Framework. Implements ViewInterface from marko/view using the Twig engine, with module-namespaced template resolution and automatic caching.

Terminal window
composer require marko/view-twig

This automatically installs marko/view. Note: marko/view-twig conflicts with marko/view-latte --- install only one view driver per project.

Configure via the view config key. Settings from marko/view (cache_directory, auto_refresh) apply to all drivers. The following are Twig-specific defaults:

config/view.php
return [
'cache_directory' => 'storage/views', // relative to the project root; created 0700
'auto_refresh' => true, // Set false in production
'extension' => '.twig',
'strict_variables' => true,
'autoescape' => 'html',
'debug' => false,
'charset' => 'UTF-8',
];

Templates are rendered using the module namespace syntax:

use Marko\View\ViewInterface;
$view->render('blog::post/index', ['posts' => $posts]);

The format is module::path/to/template where:

  • module is the module name (e.g., blog, admin)
  • path/to/template is the path within resources/views/

The render() method returns an HTTP Response object. Use renderToString() when you need the raw HTML --- for example, when rendering email bodies:

use Marko\View\ViewInterface;
$html = $view->renderToString('blog::email/comment-verification/html', $data);
$text = $view->renderToString('blog::email/comment-verification/text', $data);

All template includes use the same namespaced syntax:

{% include 'blog::post/list/item' with { post: post } %}
{% include 'blog::pagination/index' with { pagination: posts } %}

Relative paths are not supported. This ensures:

  • Consistent syntax throughout templates
  • Templates can include from any module
  • No fragile relative path dependencies

Pass variables to included templates as named parameters:

{% include 'blog::post/list/item' with { post: post, showAuthor: true } %}

Default values in the included template:

resources/views/post/list/item.twig
{# post/list/item.twig #}
{% set showAuthor = showAuthor ?? true %}
{% set linkAuthor = linkAuthor ?? true %}
<li class="post-item">
<h2><a href="/blog/{{ post.slug }}">{{ post.title }}</a></h2>
{% if showAuthor %}
<span>By {{ post.author.name }}</span>
{% endif %}
</li>

The route() function builds URLs for named routes through UrlGeneratorInterface, so templates don’t hard-code paths:

resources/views/post/list/item.twig
<a href="{{ route('blog.post.show', {slug: post.slug}) }}">{{ post.title }}</a>
<a href="{{ route('blog.index', {page: 2}) }}">Next page</a> {# /blog?page=2 #}
<link rel="canonical" href="{{ route('blog.post.show', {slug: post.slug}, true) }}">

The arguments match UrlGeneratorInterface::route(string $name, array $parameters = [], bool $absolute = false). Parameters the path doesn’t use become the query string, and true as the third argument uses APP_URL. The function isn’t marked safe, so its output is escaped like any other value (& renders as &amp;). An unknown name or a missing parameter throws UrlGenerationException. Twig wraps it in a Twig\Error\RuntimeError and the render fails.

All templates must live within at least one directory. No top-level template files.

views/
post/
index.twig # Post listing
show.twig # Single post
list/
item.twig # Reusable list item
category/
show.twig
tag/
index.twig
author/
show.twig
search/
index.twig
bar.twig # Search input
comment/
form.twig
thread.twig
pagination/
index.twig

Email templates group HTML and plain text versions together:

views/
email/
comment-verification/
html.twig # HTML version
text.twig # Plain text version
welcome/
html.twig
text.twig

TwigView implements ViewInterface from marko/view.

MethodDescription
render(string $template, array $data = []): ResponseRender a template and return an HTTP response
renderToString(string $template, array $data = []): stringRender a template and return the raw HTML string
FunctionDescription
route(string $name, array $parameters = [], bool $absolute = false): stringURL for a named route (Marko\View\Twig\RouteExtension)
OptionTypeDescription
cache_directorystringDirectory for compiled template cache (from marko/view). Defaults to storage/views under the project root; the factory refuses a directory that is world-writable or owned by another user --- see compiled-template cache directory
auto_refreshboolRecompile templates when source changes --- set false in production (from marko/view)
extensionstringTemplate file extension (default .twig)
strict_variablesboolThrow an error for undefined variables (default true)
autoescapestringAuto-escaping strategy: html, js, css, url, name, or false (default html)
debugboolEnable Twig debug mode (default false)
charsetstringTemplate charset (default UTF-8)