Template Inheritance and Reusability

Template reusability and inheritance mechanisms are here to boost your productivity because each template contains only its unique content, and repeated elements and structures are reused. We introduce three concepts: layout-inheritance, horizontal-reuse, and unit-inheritance.

The concept of Latte template inheritance is similar to PHP class inheritance. You define a parent template from which other child templates can inherit and can override parts of the parent template. It works great when elements share a common structure. Sound complicated? Don't worry, it's very easy.

Layout Inheritance {layout}

Let's look at layout template inheritance with an example. This is a parent template, let's call it layout.latte, which defines the skeleton of an HTML document:

<!doctype html>
<html lang="en">
<head>
	<title>{block title}{/block}</title>
	<link rel="stylesheet" href="style.css">
</head>
<body>
	<div id="content">
		{block content}{/block}
	</div>
	<div id="footer">
		{block footer}&copy; Copyright 2008{/block}
	</div>
</body>
</html>

The {block} tags define three blocks that child templates can fill. All the block tag does is tell the template engine that a child template may override these portions by defining its own block with the same name.

A child template might look like this:

{layout 'layout.latte'}

{block title}My amazing blog{/block}

{block content}
	<p>Welcome to my awesome homepage.</p>
{/block}

The {layout} tag is key here. It tells Latte that this template “extends” another template. When Latte renders this template, it first locates the parent template – in this case, layout.latte.

At this point, Latte notices the three block tags in layout.latte and replaces these blocks with the content of the child template. Since the child template did not define the footer block, the content from the parent template is used instead. Content within a {block} tag in the parent template is always used as a fallback.

The output might look like this:

<!doctype html>
<html lang="en">
<head>
	<title>My amazing blog</title>
	<link rel="stylesheet" href="style.css">
</head>
<body>
	<div id="content">
		<p>Welcome to my awesome homepage.</p>
	</div>
	<div id="footer">
		&copy; Copyright 2008
	</div>
</body>
</html>

In a child template, blocks can only be located at the top level or inside another block, i.e.:

{block content}
	<h1>{block title}Welcome to my awesome homepage{/block}</h1>
{/block}

Also, a block will always be created regardless of whether the surrounding {if} condition is evaluated as true or false. So, even though it might not seem like it, this template does define the block.

{if false}
	{block head}
		<meta name="robots" content="noindex, follow">
	{/block}
{/if}

If you want the output inside the block to be displayed conditionally, use the following instead:

{block head}
	{if $condition}
		<meta name="robots" content="noindex, follow">
	{/if}
{/block}

Code outside blocks in the child template is executed before rendering the layout template, so you can use it to define variables like {var $foo = bar} and propagate data throughout the inheritance chain:

{layout 'layout.latte'}
{var $robots = noindex}

...

Multilevel Inheritance

You can use as many levels of inheritance as needed. One common way of using layout inheritance is the following three-level approach:

  1. Create a layout.latte template that holds the main look and feel of your site.
  2. Create a layout-SECTIONNAME.latte template for each section of your site. For example, layout-news.latte, layout-blog.latte, etc. All these templates extend layout.latte and include styles & design specific to each section.
  3. Create individual templates for each type of page, such as a news article or blog post. These templates extend the appropriate section template.

Dynamic Layout Inheritance

You can use a variable or any PHP expression as the name of the parent template, so inheritance can behave dynamically:

{layout $standalone ? 'minimum.latte' : 'layout.latte'}

You can also use the Latte API to choose the layout template automatically.

Tips

Here are some tips for working with layout inheritance:

  • If you use {layout} in a template, it must be the first template tag in that template.
  • The layout can be found automatically (like in presenters). In this case, if the template should not have a layout, it indicates this with the {layout none} tag.
  • The {layout} tag has an alias {extends}.
  • The layout file name depends on the loader.
  • You can have as many blocks as you want. Remember that child templates do not have to define all parent blocks, so you can fill in reasonable defaults in several blocks and then define only those you need later.

Blocks {block}

See also anonymous {block}

A block provides a way to change how a certain part of a template is rendered, but it does not interfere in any way with the logic around it. Let's illustrate how a block works, and more importantly, how it doesn't work, with the following example:

{foreach $posts as $post}
{block post}
	<h1>{$post->title}</h1>
	<p>{$post->body}</p>
{/block}
{/foreach}

If you render this template, the result will be exactly the same with or without the {block} tags. Blocks have access to variables from outer scopes. They just provide a way to be overridden by a child template:

{layout 'parent.Latte'}

{block post}
	<article>
		<header>{$post->title}</header>
		<section>{$post->text}</section>
	</article>
{/block}

Now, when rendering the child template, the loop will use the block defined in the child template child.Latte instead of the one defined in parent.Latte; the executed template is then equivalent to the following:

{foreach $posts as $post}
	<article>
		<header>{$post->title}</header>
		<section>{$post->text}</section>
	</article>
{/foreach}

However, if we create a new variable inside a named block or replace the value of an existing one, the change will only be visible inside the block:

{var $foo = 'foo'}
{block post}
	{do $foo = 'new value'}
	{var $bar = 'bar'}
{/block}

foo: {$foo}                  // prints: foo
bar: {$bar ?? 'not defined'} // prints: not defined

The content of a block can be modified by filters. The following example removes all HTML and capitalizes it:

<title>{block title|stripHtml|capitalize}...{/block}</title>

The tag can also be written as an n:attribute:

<article n:block=post>
	...
</article>

Local Blocks

Every block overrides the content of the parent block with the same name – except for local blocks. They are analogous to private methods in classes. You can create a template without worrying that, due to a coincidence of block names, they might be overwritten by another template.

{block local helper}
	...
{/block}

Printing Blocks {include}

See also {include file}

To print a block in a specific place, use the {include blockname} tag:

<title>{block title}{/block}</title>

<h1>{include title}</h1>

You can also print a block from another template:

{include footer from 'main.latte'}

The rendered block does not have access to the variables of the active context, unless the block is defined in the same file where it is included. However, it does have access to global variables.

You can pass variables to the block like this:

{include footer, foo: bar, id: 123}

The block name can be a variable or any PHP expression. In this case, add the keyword block before the variable so that Latte knows at compile time that it's a block and not an included template, whose name could also be in a variable:

{var $name = footer}
{include block $name}

A block can also be rendered within itself, which is useful, for example, when rendering a tree structure:

{define menu, $items}
<ul>
	{foreach $items as $item}
		<li>
		{if is_array($item)}
			{include menu, $item}
		{else}
			{$item}
		{/if}
		</li>
	{/foreach}
</ul>
{/define}

Instead of {include menu, ...}, we can also write {include this, ...}, where this means the current block.

The rendered block content can be modified by filters. The following example removes all HTML and capitalizes it:

<title>{include heading|stripHtml|capitalize}</title>

Parent Block

If you need to print the content of the block from the parent template, use {include parent}. This is useful if you want to supplement the content of the parent block instead of completely overriding it.

{block footer}
	{include parent}
	<a href="https://github.com/nette">GitHub</a>
	<a href="https://twitter.com/nettefw">Twitter</a>
{/block}

Definitions {define}

In addition to blocks, Latte also has “definitions”. In common programming languages, they would be comparable to functions. They are useful for reusing template fragments to avoid repetition.

Latte tries to keep things simple, so basically, definitions are the same as blocks, and everything said about blocks also applies to definitions. They differ from blocks in that:

  1. they are enclosed in {define} tags
  2. they are rendered only when inserted via {include}
  3. you can define parameters for them, similar to functions in PHP
{block foo}<p>Hello</p>{/block}
{* prints: <p>Hello</p> *}

{define bar}<p>World</p>{/define}
{* prints nothing *}

{include bar}
{* prints: <p>World</p> *}

Imagine you have a helper template with a collection of definitions on how to draw HTML forms.

{define input, $name, $value, $type = 'text'}
	<input type={$type} name={$name} value={$value}>
{/define}

{define textarea, $name, $value}
	<textarea name={$name}>{$value}</textarea>
{/define}

Arguments are always optional with a default value of null, unless a default value is specified (here 'text' is the default value for $type). Parameter types can also be declared: {define input, string $name, ...}.

The template with the definitions is loaded using {import}. The definitions themselves are rendered in the same way as blocks:

<p>{include input, 'password', null, 'password'}</p>
<p>{include textarea, 'comment'}</p>

Definitions do not have access to the variables of the active context, but they do have access to global variables.

Dynamic Block Names

Latte allows great flexibility in defining blocks because the block name can be any PHP expression. This example defines three blocks named hi-Peter, hi-John, and hi-Mary:

{foreach [Peter, John, Mary] as $name}
	{block "hi-$name"}Hi, I am {$name}.{/block}
{/foreach}

In the child template, we can then redefine, for example, just one block:

{block hi-John}Hello. I am {$name}.{/block}

So the output will look like this:

Hi, I am Peter.
Hello. I am John.
Hi, I am Mary.

Checking Block Existence {ifset}

See also {ifset $var}

Use the {ifset blockname} test to check if a block (or multiple blocks) exists in the current context:

{ifset footer}
	...
{/ifset}

{ifset footer, header, main}
	...
{/ifset}

The block name can be a variable or any PHP expression. In this case, add the keyword block before the variable to clarify that it's not a check for the existence of variables:

{ifset block $name}
	...
{/ifset}

The existence of blocks is also checked by the hasBlock() function:

{if hasBlock(header) || hasBlock(footer)}
	...
{/if}

Tips

Some tips for working with blocks:

  • The last top-level block does not need a closing tag (the block ends with the end of the document). This simplifies writing child templates that contain one primary block.
  • For better readability, you can optionally give the block name in the {/block} tag, for example {/block footer}. However, the name must match the block name. In larger templates, this technique helps you see which block tags are being closed.
  • You can’t directly define multiple block tags with the same name in the same template. But this can be achieved using dynamic block names.
  • You can use n:attributes to define blocks like <h1 n:block=title>Welcome to my awesome homepage</h1>
  • Blocks can also be used without names only to apply filters to the output: {block|strip} hello {/block}

Horizontal Reuse {import}

Horizontal reuse is the third mechanism for reuse and inheritance in Latte. It allows loading blocks from other templates. It's similar to creating a file with helper functions in PHP and then loading it using require.

While template layout inheritance is one of Latte's most powerful features, it is limited to simple inheritance – a template can only extend one other template. Horizontal reuse is a way to achieve multiple inheritance.

Let's have a file with block definitions:

{block sidebar}...{/block}

{block menu}...{/block}

Using the {import} command, we import all the blocks and definitions defined in blocks.latte into another template:

{import 'blocks.latte'}

{* sidebar and menu blocks can now be used *}

If you import blocks in the parent template (i.e., use {import} in layout.latte), the blocks will also be available in all child templates, which is very practical.

The template that is intended to be imported (e.g. blocks.latte) must not extend another template, i.e., use {layout}. However, it can import other templates.

The {import} tag should be the first template tag after {layout}. The template name can be any PHP expression:

{import $ajax ? 'ajax.latte' : 'not-ajax.latte'}

You can use as many {import} statements as you want in a template. If two imported templates define the same block, the first one wins. However, the main template has the highest priority and can override any imported block.

The content of overwritten blocks can be preserved by inserting the block in the same way as a parent-block:

{layout 'layout.latte'}

{import 'blocks.latte'}

{block sidebar}
	{include parent}
{/block}

{block title}...{/block}
{block content}...{/block}

In this example, {include parent} calls the sidebar block from the blocks.latte template.

Unit Inheritance {embed}

Unit inheritance extends the idea of layout inheritance to the level of content fragments. While layout inheritance works with “document skeletons” brought to life by child templates, unit inheritance allows you to create skeletons for smaller units of content and reuse them wherever you want.

In unit inheritance, the {embed} tag is key. It combines the behavior of {include} and {layout}. It allows you to embed the content of another template or block and optionally pass variables, just like {include}. It also allows you to override any block defined inside the embedded template, like {layout}.

For example, let's use an accordion element. Take a look at the element skeleton stored in the collapsible.latte template:

<section class="collapsible {$modifierClass}">
	<h4 class="collapsible__title">
		{block title}{/block}
	</h4>

	<div class="collapsible__content">
		{block content}{/block}
	</div>
</section>

The {block} tags define two blocks that child templates can fill. Yes, just like in the case of the parent template in layout inheritance. You also see the $modifierClass variable.

Let's use our element in a template. This is where {embed} comes in. It's an extremely powerful tag that allows us to do all these things: embed the element's template content, add variables to it, and add blocks with custom HTML to it:

{embed 'collapsible.latte', modifierClass: my-style}
	{block title}
		Hello World
	{/block}

	{block content}
		<p>Lorem ipsum dolor sit amet, consectetuer adipiscing
		elit. Nunc dapibus tortor vel mi dapibus sollicitudin.</p>
	{/block}
{/embed}

The output might look like this:

<section class="collapsible my-style">
	<h4 class="collapsible__title">
		Hello World
	</h4>

	<div class="collapsible__content">
		<p>Lorem ipsum dolor sit amet, consectetuer adipiscing
		elit. Nunc dapibus tortor vel mi dapibus sollicitudin.</p>
	</div>
</section>

Blocks inside embed tags form a separate layer independent of other blocks. Therefore, they can have the same name as a block outside the embed and are not affected in any way. Using the include tag inside {embed} tags, you can insert blocks created here, blocks from the embedded template (which are not local), and also blocks from the main template that are local. You can also import blocks from other files:

{block outer}…{/block}
{block local hello}…{/block}

{embed 'collapsible.latte', modifierClass: my-style}
	{import 'blocks.latte'}

	{block inner}…{/block}

	{block title}
		{include inner} {* works, block is defined inside embed *}
		{include hello} {* works, block is local in this template *}
		{include content} {* works, block is defined in embedded template *}
		{include aBlockDefinedInImportedTemplate} {* works *}
		{include outer} {* does not work! - block is in outer layer *}
	{/block}
{/embed}

Embedded templates do not have access to the variables of the active context, but they do have access to global variables.

Using {embed}, you can embed not only templates but also other blocks, so the previous example could be written this way:

{define collapsible}
<section class="collapsible {$modifierClass}">
	<h4 class="collapsible__title">
		{block title}{/block}
	</h4>
	...
</section>
{/define}


{embed collapsible, modifierClass: my-style}
	{block title}
		Hello World
	{/block}
	...
{/embed}

If we pass an expression to {embed} and it's not clear whether it's a block name or a file name, add the keyword block or file:

{embed block $name} ... {/embed}

Use Cases

There are various types of inheritance and code reuse in Latte. Let's summarize the main concepts for better clarity:

{include template}

Use Case: Using header.latte and footer.latte inside layout.latte.

header.latte

<nav>
   <div>Home</div>
   <div>About</div>
</nav>

footer.latte

<footer>
   <div>Copyright</div>
</footer>

layout.latte

{include 'header.latte'}

<main>{block main}{/block}</main>

{include 'footer.latte'}

{layout}

Use Case: Extending layout.latte inside homepage.latte and about.latte.

layout.latte

{include 'header.latte'}

<main>{block main}{/block}</main>

{include 'footer.latte'}

homepage.latte

{layout 'layout.latte'}

{block main}
	<p>Homepage</p>
{/block}

about.latte

{layout 'layout.latte'}

{block main}
	<p>About page</p>
{/block}

{import}

Use Case: Using sidebar.latte in single.product.latte and single.service.latte.

sidebar.latte

{block sidebar}<aside>This is sidebar</aside>{/block}

single.product.latte

{layout 'product.layout.latte'}

{import 'sidebar.latte'}

{block main}<main>Product page</main>{/block}

single.service.latte

{layout 'service.layout.latte'}

{import 'sidebar.latte'}

{block main}<main>Service page</main>{/block}

{define}

Use Case: Functions that receive variables and render something.

form.latte

{define form-input, $name, $value, $type = 'text'}
	<input type={$type} name={$name} value={$value}>
{/define}

profile.service.latte

{import 'form.latte'}

<form action="" method="post">
	<div>{include form-input, username}</div>
	<div>{include form-input, password}</div>
	<div>{include form-input, submit, Submit, submit}</div>
</form>

{embed}

Use Case: Embedding pagination.latte into product.table.latte and service.table.latte.

pagination.latte

<div id="pagination">
	<div>{block first}{/block}</div>

	{for $i = $min + 1; $i < $max - 1; $i++}
		<div>{$i}</div>
	{/for}

	<div>{block last}{/block}</div>
</div>

product.table.latte

{embed 'pagination.latte', min: 1, max: $products->count}
	{block first}First Product Page{/block}
	{block last}Last Product Page{/block}
{/embed}

service.table.latte

{embed 'pagination.latte', min: 1, max: $services->count}
	{block first}First Service Page{/block}
	{block last}Last Service Page{/block}
{/embed}
version: 3.0 2.x