Template Inheritance and Reusability

Template reusability and inheritance mechanisms are here to boost your productivity because each template contains only its unique contents and the 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 that other child templates can extend from and can override parts of the parent template. It works great when elements share a common structure. Sounds complicated? Don't worry, it's not.

Layout Inheritance {layout}

Let’s look at layout template inheritance by starting with an example. This is a parent template which we’ll call for example layout.latte and it defines an HTML skeleton 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 in. All the block tag does is tell the template engine that a child template may override those portions of the template by defining their own block of 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 the key here. It tells the template engine that this template “extends” another template. When Latte renderes this template, first it locates the parent – in this case, layout.latte.

At that point, the template engine will notice the three block tags in layout.latte and replace those blocks with the contents of the child template. Note that since the child template didn’t define the footer block, the content from the parent template is used instead. Content within a {block} tag in a parent template is always used as a fallback.

The output might look like:

<!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 either at the top level or inside another block, ie:

{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 to be true or false. Contrary to what you might think, this template does define a block.

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

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

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

Data outside of blocks in a child template are executed before the layout template is rendered, thus you can use it to define variables like {var $foo = bar} and propagate data to the whole 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. These templates all extend layout.latte and include section-specific styles/design.
  3. Create individual templates for each type of page, such as a news article or blog entry. 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 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.
  • Layout can be searched automatically (like in presenters). In this case, if the template should not have a layout, it will indicate this with the {layout none} tag.
  • Tag {layout} has alias {extends}.
  • The filename of the extended template depends on the template loader.
  • You can have as many blocks as you want. Remember, child templates don’t have to define all parent blocks, so you can fill in reasonable defaults in a number of blocks, then only define the ones 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 take the following example to illustrate how a block works and more importantly, how it does not work:

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

If you render this template, the result would be exactly the same with or without the block tags. Blocks have access to variables from outer scopes. It is just a way to make it overridable 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 is going to use the block defined in the child template child.Latte instead of the one defined in the base one parent.Latte; the executed template is then equivalent to the following one:

{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 a value of existing one, the change will be visible only 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

Contents of block can be modified by filters. The following example removes all HTML and title-cases it:

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

The tag can also be written as n:attribute:

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

Local Blocks

Every block overrides content of parent block of the same name. Except for local blocks. They are something like private methods in class. You can create a template without worrying that – due to coincidence of block names – they would be overwritten by a second 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 display a block from another template:

{include footer from 'main.latte'}

Printed blocks do not have access to the variables of the active context, except if the block is defined in the same file where it is included. However they do have access to the global variables.

You can pass variables to the block in the following way:

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

You can use a variable or any expression in PHP as the block name. In this case, add the keyword block before the variable, so that it is known at compile-time that it is a block, and not insert template, whose name could also be in the variable:

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

A block can also be printed inside 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.

Printed content can be modified by filters. The following example removes all HTML and title-cases it:

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

Parent Block

If you need to print the content of the block from the parent template, the {include parent} statement will do the trick. This is useful if you want to add to the contents of a 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 the blocks, there are also “definitions” in Latte. They are comparable with functions in regular programming languages. They are useful to reuse template fragments to not repeat yourself.

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 tags {define}
  2. they are only rendered when they are inserted via {include}
  3. you can define parameters for them like 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 of a definition are always optional with default value null, unless 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 the blocks:

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

Definitions don’t have access to the variables of the active context, but they 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}

For example, we can redefine only one block in a child template:

{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 more blocks) exists in the current context:

{ifset footer}
	...
{/ifset}

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

You can use a variable or any expression in PHP as the block name. In this case, add the keyword block before the variable to make it clear that it is not the variable that is checked:

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

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

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

Tips

Here are some tips for working with blocks:

  • The last top-level block does not need to have closing tag (block ends with the end of the document). This simplifies the writing of child templates with one primary block.
  • For extra readability, you can optionally give a name to your {/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 the filters to the output: {block|strip} hello {/block}

Horizontal Reuse {import}

The horizontal reuse is a third reusability and inheritance mechanism in Latte. It allows you to load blocks from other templates. It's similar to creating a PHP file with helper functions or a trait.

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 set of block definitions:

{block sidebar}...{/block}

{block menu}...{/block}

Using the {import} command, 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 the blocks in the parent template (i.e. use {import} in layout.latte), the blocks will be available in all child templates as well, which is very handy.

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 any given template. If two imported templates define the same block, the first one wins. However, the highest priority is given to the main template, which can overwrite any imported block.

All overridden blocks can be included gradually by inserting them as parent block:

{layout 'layout.latte'}

{import 'blocks.latte'}

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

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

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

Unit Inheritance {embed}

The unit inheritance takes the idea of layout inheritance to the level of content fragments. While layout inheritance works with “document skeletons”, which are brought to life by child templates, the unit inheritance allows you to create skeletons for smaller units of content and reuse them anywhere you like.

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

For example, we are going to use the collapsible accordion element. Let’s take a look at the element skeleton in the template collapsible.latte:

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

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

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

Let's use our element in a template. This is where {embed} comes in. It’s a super powerful piece of kit that lets us do all the things: include an element's template contents, 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:

<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 the block outside the embed and are not affected in any way. Using the tag include inside {embed} tags you can insert blocks created here, blocks from an embedded template (which are not local), and also blocks from the main template which 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 the global variables.

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

{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 is not clear whether it is a block or 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 more clarity:

{include template}

Use Case: Using header.latte & 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 & 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: sidebar.latte in single.product.latte & 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: A function which gets some variables and outputs some markup.

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 in product.table.latte & 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