Nette Documentation Preview

Template Inheritance and Reusability

Template reusability and inheritance mechanisms are here to boosts 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 defines three blocks that child templates can fill in. All the block tag does is to 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 contents 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 in 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 a 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.
  • 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}

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:

{* parent.Latte *}
{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:

{* child.Latte *}
{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>

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 second template.

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

Printing Blocks {include}

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

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

<h1>{include title}</h1>

The {include} tag is also used to include whole templates or to display one block from another template:

{include footer from 'main.latte'}

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

You can pass variables this way:

{* since Latte 2.9 *}
{include footer, foo: bar, id: 123}

{* before Latte 2.9 *}
{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}

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 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 do things easily, so basically the definitions are the same as blocks, and everything that is said about blocks also applies to definitions. It differs from blocks in only three ways:

  1. they can accept arguments
  2. they can't have filters
  3. they are enclosed in tags {define} and the content inside these tags is not send to output until you include them. Thanks to that, you can create them anywhere:
{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 having a generic helper template that defines how to render HTML forms via definitions:

{* forms.latte *}
{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 definitions are always optional with default value null, unless default value is specified (here text is the default value for $type, possible since Latte 2.9.1). As of Latte 2.7, parameter types can also be declared: {define input, string $name, ...}.

Definitions don’t have access to the variables of the active context, but they have access to global variables.

They are included the same way as block:

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

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 Peter, John and Mary:

{* parent.latte *}
{foreach [Peter, John, Mary] as $name}
	{block $name}Hi, I am {$name}.{/block}
{/foreach}

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

{* child.latte *}
{block 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}

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}

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, which 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.

Although layout template inheritance is one of the most powerful features of Latte, it is limited to single inheritance; a template can only extend one other template. This limitation makes template inheritance simple to understand and easy to debug:

{layout 'layout.latte'}

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

Horizontal reuse is a way to achieve the same goal as multiple inheritance, but without the associated complexity:

{layout 'layout.latte'}

{import 'blocks.latte'}

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

The {import} statement tells Latte to import all blocks and definitions defined in blocks.latte into the current template.

{* blocks.latte *}

{block sidebar}...{/block}

In this example, the {import} statement imports the sidebar block into the main template.

The imported template must not extend another template, and its body should be empty. However, the imported template 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 'base.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 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 $modifierClass variable.

Let's use our element in template. This is where {embed} comes in. It’s a super powerful piece of kit that lets us do all the things: include 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 here created, blocks from embedded template (which are not local), and also blocks from 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}

Embeded templates have not access to the variables of the active context, but they 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 clearance:

{include template}

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

header.latte

<nav>
   <div>Homepage</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}