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 of inheritance: layout, horizontal and unit.

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 is very basic.

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:

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

Contrary to what you might think, this template does define a block. 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 head}

Data outside of a blocks in a child template is 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

As the template name can be any valid PHP expression, it’s possible to make the inheritance mechanism dynamic:

{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>

Including 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'}

Included 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}

The block name can be any valid PHP expression. You only need to add the keyword block to make it clear that it is block and distinguish it from tags:

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

Blocks can also include themselves which, for example, is suitable when rendering a tree structured menu:

{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 write {include this, ...} where this means current block.

Included 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}

Local Blocks

Every block override the content of parent block of the same name. Except for local blocks. They are something like private methods in class. You can create the template without worrying that due to coincidence of block names they has been overwritten by second template.

{block local helper}
	...
{/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> *}

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

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

Imagine having a generic helper template that define 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 the global variables.

They are included the same way as block:

<p>{include input, 'password', type: '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 two blocks named John and Doe:

{foreach [John, Doe] as $name}
	{block $name}Hi, I am {$name}{/block}
{/foreach}

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}

The block name can be any valid PHP expression. You only need to add the keyword block 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 Inheritance {import}

The horizontal inheritance (or reuse) is a third reusability and inheritance mechanism in Latte. It allows you to load blocks from third 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 inheritance 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.html *}

{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 it's body should be empty. However 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'}

The main template can also override any imported block. If the template already defines the sidebar block, then the one defined in blocks.latte is overridden and can be included as parent block:

{layout 'base.html'}

{import 'blocks.html'}

{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.html template.

You can use as many {import} statements as you want in any given template. If two imported templates define the same block, the latest one wins.

Unit Inheritance {embed}

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

In unit inheritance the {embed} tag is the key. It combines the behavior of {include template} and {layout}. It allows you to include another template’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, as 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.

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}
Improve this page