marko/view-latte
Latte templating driver for the Marko Framework. Implements ViewInterface from marko/view using the Latte engine, with module-namespaced template resolution, automatic caching, and strict types.
Installation
Section titled “Installation”composer require marko/view-latteThis automatically installs marko/view. Note: marko/view-latte conflicts with marko/view-twig --- install only one view driver per project.
Configuration
Section titled “Configuration”Configure via the view config key. Settings from marko/view (cache_directory, auto_refresh) apply to all drivers. The following are Latte-specific defaults:
return [ 'cache_directory' => 'storage/views', // relative to the project root; created 0700 'auto_refresh' => true, // false in production, with view:cache on deploy 'extension' => '.latte', 'strict_types' => true,];Rendering Templates
Section titled “Rendering Templates”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:
moduleis the module name (e.g.,blog,admin)path/to/templateis the path withinresources/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);Template Includes
Section titled “Template Includes”All template includes use the same namespaced syntax:
{include 'blog::post/list/item', post: $post}{include 'blog::pagination/index', pagination: $posts}Relative paths (../) are not supported. This ensures:
- Consistent syntax throughout templates
- Templates can include from any module
- No fragile relative path dependencies
Passing Data to Includes
Section titled “Passing Data to Includes”Pass variables to included templates as named parameters:
{include 'blog::post/list/item', post: $post, showAuthor: true}Default values in the included template:
{* post/list/item.latte *}{default $showAuthor = true}{default $linkAuthor = true}
<li class="post-item"> <h2><a href="/blog/{$post->slug}">{$post->title}</a></h2> {if $showAuthor} <span>By {$post->getAuthor()->name}</span> {/if}</li>Linking to Named Routes
Section titled “Linking to Named Routes”The route() function builds URLs for named routes through UrlGeneratorInterface, so templates don’t hard-code paths:
<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], absolute: true)}>The arguments match UrlGeneratorInterface::route(string $name, array $parameters = [], bool $absolute = false). Parameters the path doesn’t use become the query string, and absolute: true uses APP_URL. Output is escaped like any other expression, so & in a query string renders as & inside an attribute. An unknown name or a missing parameter throws UrlGenerationException and fails the render.
signedRoute() builds a signed URL through UrlSignerInterface, for links that leave the app, such as an unsubscribe link in an email template. It is absolute by default. Pass a DateTimeImmutable view variable as expiresAt for a link that expires:
<a href={signedRoute('newsletter.unsubscribe', [id: $subscriber->id])}>Unsubscribe</a><a href={signedRoute('share.show', [token: $token], expiresAt: $expiresAt)}>Open (valid for 24 hours)</a>route() comes from RouteFunctionProvider and signedRoute() from SignedRouteFunctionProvider, both in marko/view. Every template function provider is registered with Latte by TemplateFunctionExtension: a function declared isSafeHtml has its result wrapped in Latte\Runtime\Html and prints unescaped, every other result is escaped. Shared view data is merged into every render() and renderToString() call.
Precompiling Templates
Section titled “Precompiling Templates”marko view:cache compiles every template into view.cache_directory ahead of time. This package binds LatteTemplatePrecompiler as the TemplatePrecompilerInterface the command uses:
marko view:cacheIt builds an engine with LatteEngineFactory::createWithAutoRefresh(), from the same factory as rendering, and calls Latte\Engine::warmupCache(). The factory gives every engine it builds the module loader, so the compiled file lands under the key rendering looks up: the template’s resolved absolute path. A Latte\CompileException is rethrown as TemplatePrecompileException with the template name, file and line. A broken template fails the command with its file and line:
Template 'blog::post/show' failed to compile: Unexpected tag {notATag} (on line 2 at column 4) in /var/www/app/blog/resources/views/post/show.latte:2Only the template that wins resolution is compiled, so an app/ override is compiled and the module template it shadows is skipped. Auto-refresh is always on while precompiling, so a template changed since the last compile is compiled again even when auto_refresh is false.
In production, run view:cache on every deploy, on the server at the path the app runs from, and set auto_refresh to false. Compiled files are keyed by absolute path, so templates compiled during an image build at another path are not reused. See deploying to production.
Template Organization
Section titled “Template Organization”All templates must live within at least one directory. No top-level template files.
Standard Structure
Section titled “Standard Structure”views/ post/ index.latte # Post listing show.latte # Single post list/ item.latte # Reusable list item category/ show.latte tag/ index.latte author/ show.latte search/ index.latte bar.latte # Search input comment/ form.latte thread.latte pagination/ index.latteEmail Templates
Section titled “Email Templates”Email templates group HTML and plain text versions together:
views/ email/ comment-verification/ html.latte # HTML version text.latte # Plain text version welcome/ html.latte text.latteAPI Reference
Section titled “API Reference”LatteView implements ViewInterface and LatteTemplatePrecompiler implements TemplatePrecompilerInterface, both from marko/view.
Key Methods
Section titled “Key Methods”| Method | Description |
|---|---|
render(string $template, array $data = []): Response | Render a template and return an HTTP response |
renderToString(string $template, array $data = []): string | Render a template and return the raw HTML string |
LatteTemplatePrecompiler::precompile(string $template): void | Compile one template into the cache; throws TemplatePrecompileException on a syntax error |
LatteEngineFactory::create(): Engine | Build an engine that loads module::path templates and refreshes compiled templates per auto_refresh |
LatteEngineFactory::createWithAutoRefresh(): Engine | Build the same engine with auto-refresh always on |
Template Functions
Section titled “Template Functions”| Function | Description |
|---|---|
route(string $name, array $parameters = [], bool $absolute = false): string | URL for a named route (Marko\View\Functions\RouteFunctionProvider in marko/view) |
signedRoute(string $name, array $parameters = [], ?DateTimeImmutable $expiresAt = null, bool $absolute = true): string | Signed URL for a named route (Marko\View\Functions\SignedRouteFunctionProvider in marko/view) |
Functions from every other discovered #[TemplateFunctions] provider are available the same way.
Configuration Options
Section titled “Configuration Options”| Option | Type | Description |
|---|---|---|
cache_directory | string | Directory 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_refresh | bool | Recompile templates when source changes --- set false in production and run view:cache on deploy (from marko/view) |
extension | string | Template file extension (default .latte) |
strict_types | bool | Enable strict type checking in templates (default true) |