Nette Documentation Preview

syntax
Наследяване на шаблони и възможност за повторно използване
**********************************************************

.[perex]
Механизмите за повторна употреба и наследяване на шаблони увеличават производителността ви, тъй като всеки шаблон съдържа само уникално съдържание, а повтарящите се елементи и структури се използват повторно. Въвеждаме три концепции: [наследяване на оформлението |#Layout-Inheritance], [хоризонтално повторно използване |#Horizontal-Reuse] и [наследяване на единици |#Unit-Inheritance].

Концепцията за наследяване на шаблони Latte е подобна на тази за наследяване на класове в PHP. Дефинирате **шаблон-родител**, от който други **наследени шаблони** могат да надграждат и да отменят части от шаблона-родител. Това работи чудесно, когато елементите имат обща структура. Звучи сложно? Не се притеснявайте, не е така.


Наследяване на оформлението `{layout}` .{toc: Layout Inheritance}
=================================================================

Нека разгледаме наследяването на шаблона на оформлението с пример. Това е родителският шаблон, който в примера ще наречем `layout.latte`, и той определя HTML скелета на документа.

```latte
<!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>
```

Тагът `{block}` дефинира три блока, които подчинените шаблони могат да попълват. Единственото, което тагът за блок прави, е да укаже на шаблониращата машина, че подчиненият шаблон може да замени тези части на шаблона, като дефинира свой собствен блок със същото име.

Един шаблон на дете може да изглежда по следния начин:

```latte
{layout 'layout.latte'}

{block title}My amazing blog{/block}

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

Ключовият етикет тук е `{layout}`. Той указва на шаблониращата машина, че този шаблон "разширява" друг шаблон. Когато Latte визуализира този шаблон, той първо намира родителя - в случая `layout.latte`.

В този момент механизмът за шаблониране ще забележи три блокови тага в `layout.latte` и ще замени тези блокове със съдържанието на подчинения шаблон. Имайте предвид, че тъй като шаблонът на детето не е дефинирал блок *footer*, вместо него се използва съдържанието на шаблона на родителя. Съдържанието в тага `{block}` в родителския шаблон винаги се използва като резервен вариант.

Изходът може да изглежда по следния начин:

```latte
<!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>
```

В подчинен шаблон блоковете могат да се поставят само на горното ниво или в друг блок, т.е:

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

Освен това блокът винаги ще бъде създаден, независимо дали заобикалящото го условие `{if}` е оценено като вярно или невярно. Противно на това, което си мислите, този шаблон дефинира блок.

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

Ако искате изходът в блока да се показва условно, използвайте следното:

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

Извънблоковите данни в подчинения шаблон се изпълняват преди шаблонът за оформление да бъде визуализиран, така че можете да го използвате, за да дефинирате променливи от тип `{var $foo = bar}` и да разпространявате данните по цялата верига на наследяване:

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

...
```


Наследяване на няколко нива .[#toc-multilevel-inheritance]
----------------------------------------------------------
Можете да използвате толкова нива на наследяване, колкото ви е необходимо. Един от разпространените начини за използване на наследяване на оформлението е следният подход на три нива:

1) Създайте шаблон `layout.latte`, в който се съхранява основният външен вид на сайта ви.
2) Създайте шаблон `layout-SECTIONNAME.latte` за всеки раздел на сайта си. Например `layout-news.latte`, `layout-blog.latte` и т.н. Всички тези шаблони разширяват `layout.latte` и включват стилове/дизайни за всеки раздел.
3) Създайте отделни шаблони за всеки тип страница, например новинарска статия или публикация в блог. Тези шаблони разширяват съответния шаблон на раздела.


Динамично наследяване на оформлението .[#toc-dynamic-layout-inheritance]
------------------------------------------------------------------------
Можете да използвате променлива или какъвто и да е израз на PHP като име на родителския шаблон, така че наследяването да се извършва динамично:

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

Можете също така да използвате приложния програмен интерфейс Latte API за [автоматично |develop#Automatic-Layout-Lookup] избиране на шаблона за оформление.


Съвети .[#toc-tips]
-------------------
Ето няколко съвета за работа с наследяване на оформлението:

- Ако използвате `{layout}` в шаблон, той трябва да бъде първият таг на шаблона в този шаблон.

- Макетът може да се [търси автоматично |develop#automatic-layout-lookup] (както в [презентаторите |application:templates#search-for-templates]). В този случай, ако шаблонът не трябва да има оформление, той ще посочи това с тага `{layout none}`.

- Тагът `{layout}` има псевдоним `{extends}`.

- Името на разширения файл на шаблона зависи от [програмата за зареждане на шаблони |extending-latte#Loaders].

- Можете да имате толкова блокове, колкото искате. Не забравяйте, че не е задължително подчинените шаблони да дефинират всички родителски блокове, така че можете да въведете разумни стойности по подразбиране в няколко блока и след това да дефинирате само тези, които са ви необходими по-късно.


Блокове `{block}` .{toc: Blocks}
================================

.[note]
Вижте също анонимни [`{block}` |tags#block]

Блокът ви позволява да променяте показването на определена част от шаблона, но не се намесва по никакъв начин в заобикалящата го логика. Нека разгледаме следния пример, за да илюстрираме как работи блокът и, което е по-важно, как не работи:

```latte .{file: parent.latte}
{foreach $posts as $post}
{block post}
	<h1>{$post->title}</h1>
	<p>{$post->body}</p>
{/block}
{/foreach}
```

Ако покажете този модел, резултатът е абсолютно същият със или без блокови тагове. Блоковете имат достъп до променливи от външни диапазони. Това е просто начин да се направи така, че да може да се пренастройва за шаблона на детето:

```latte .{file: child.latte}
{layout 'parent.Latte'}

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

Сега при визуализиране на шаблона на детето цикълът ще използва блока, дефиниран в шаблона на детето `child.Latte`, вместо блока, дефиниран в базовия шаблон `parent.Latte`; изпълненият шаблон ще бъде еквивалентен на следния:

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

Ако обаче създадем нова променлива вътре в именуван блок или заменим стойността на съществуваща променлива, промяната ще бъде видима само вътре в блока:

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

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

Съдържанието на блока може да се променя с помощта на [филтри |syntax#Filters]. В следващия пример целият HTML е премахнат и е дадено заглавието:

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

Тагът може да се запише и като [n:attribute |syntax#n-attributes]:

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


Местни блокове .[#toc-local-blocks]
-----------------------------------

Всеки блок отменя съдържанието на родителския блок със същото име. С изключение на местните блокове. Те донякъде приличат на частните методи в класа. Можете да създадете шаблон, без да се притеснявате, че той ще бъде презаписан от втори шаблон поради съвпадащите имена на блоковете.

```latte
{block local helper}
	...
{/block}
```


Блокове за печат `{include}` .{toc: Printing Blocks}
----------------------------------------------------

.[note]
Вижте също [`{include file}` |tags#include]

За да отпечатате блок на определено място, използвайте маркера `{include blockname}`:

```latte
<title>{block title}{/block}</title>

<h1>{include title}</h1>
```

Можете също така да изведете блок от друг шаблон:

```latte
{include footer from 'main.latte'}
```

Производните блокове нямат достъп до променливите на активния контекст, освен ако блокът не е дефиниран в същия файл, в който е включен. Те обаче имат достъп до глобални променливи.

Можете да предавате променливи на блока по следния начин:

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

За име на блока можете да използвате променлива или друг израз в PHP. В този случай добавете ключовата дума `block` преди променливата, така че по време на компилиране да се знае, че това е блок, а не [шаблон за вмъкване |tags#include], чието име също може да бъде в променливата:

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

Блокът може да бъде отпечатан и вътре в себе си, което е полезно например при визуализиране на дървовидна структура:

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

Вместо `{include menu, ...}` можете да напишете и `{include this, ...}`, където `this` означава текущия блок.

Изходното съдържание може да се променя с помощта на [филтри |syntax#Filters]. В следващия пример целият HTML е премахнат и е вмъкнато заглавието:

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


Родителски блок .[#toc-parent-block]
------------------------------------

Ако трябва да визуализирате съдържанието на родителски блок от родителски шаблон, операторът `{include parent}` е полезен. Това е полезно, ако искате да разширите съдържанието на родителския блок, а не да го замените изцяло.

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


Определения `{define}` .{toc: Definitions}
------------------------------------------

Освен блокове в Latte има и "дефиниции". Те могат да се сравнят с функциите в традиционните езици за програмиране. Те са полезни за повторно използване на фрагменти от шаблони, така че да не се повтарят.

Latte се опитва да опрости нещата, така че основно дефинициите са същите като блоковете и **всичко, казано за блоковете, се отнася и за дефинициите**. Те се различават от блоковете по това:

1) те са затворени в тагове `{define}`
2) визуализират се само когато са вмъкнати чрез `{include}`
3) можете да задавате параметри за тях като за функциите в PHP

```latte
{block foo}<p>Hello</p>{/block}
{* prints: <p>Hello</p> *}

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

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

Представете си, че имате помощен шаблон с набор от дефиниции за това как да изготвяте HTML форми.

```latte .{file: 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}
```

Аргументите на дадена дефиниция са винаги незадължителни със стойност по подразбиране `null`, освен ако не е посочена стойност по подразбиране (тук `'text'` е стойността по подразбиране за `$type`). Типовете параметри също могат да бъдат декларирани: `{define input, string $name, ...}`.

Шаблонът с дефинициите се зарежда с помощта на [`{import}` |#horizontal-reuse]. Самите дефиниции се визуализират по [същия начин, както блоковете |#Printing Blocks]:

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

Дефинициите нямат достъп до променливите на активния контекст, но имат достъп до глобалните променливи.


Динамични имена на блокове .[#toc-dynamic-block-names]
------------------------------------------------------

Latte позволява много гъвкаво именуване на блокове, тъй като името на блока може да бъде всеки израз на PHP. В този пример са дефинирани три блока с имена `hi-Peter`, `hi-John` и `hi-Mary`:

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

Например, можем да заменим само един блок в шаблона на детето:

```latte .{file: child.latte}
{block hi-John}Hello. I am {$name}.{/block}
```

Така изходът ще изглежда по следния начин:

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


Проверка на съществуването на блока `{ifset}` .{toc: Checking Block Existence}
------------------------------------------------------------------------------

.[note]
Вижте също [`{ifset $var}` |tags#ifset-elseifset]

Използвайте теста `{ifset blockname}`, за да проверите дали даден блок (или няколко блока) съществува в текущия контекст:

```latte
{ifset footer}
	...
{/ifset}

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

За име на блока можете да използвате променлива или друг израз в PHP. В този случай добавете ключовата дума `block` пред променливата, за да стане ясно, че [тя |tags#ifset-elseifset] не е [тази, която |tags#ifset-elseifset] се тества:

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

Съществуването на блокове се връща и от функцията [`hasBlock()` |functions#hasBlock]:

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


Съвети .[#toc-tips]
-------------------
Ето няколко съвета за работа с блокове:

- Последният блок от най-високо ниво не трябва да има затварящ таг (блокът завършва с края на документа). Това улеснява писането на шаблони за деца с един основен блок.

- За по-голяма прегледност можете по желание да дадете име на тага `{/block}`, например `{/block footer}`. Името обаче трябва да съвпада с името на блока. В по-големи шаблони тази техника помага да се види кои блокови тагове са затворени.

- Не можете да дефинирате директно няколко блокови тага с едно и също име в един шаблон. Но това може да се постигне чрез използване на [динамични имена на блокове |#Dynamic-Block-Names].

- Можете да използвате [n:attributes, |syntax#n-attributes] за да дефинирате блокове като `<h1 n:block=title>Welcome to my awesome homepage</h1>`

- Блоковете могат да се използват и без имена, само за да се прилагат [филтри |syntax#Filters] към изхода: `{block|strip} hello {/block}`


Хоризонтална повторна употреба `{import}` .{toc: Horizontal Reuse}
==================================================================

Хоризонталната повторна употреба е третият механизъм за повторна употреба и наследяване в Latte. Той позволява зареждане на блокове от други шаблони. Това е подобно на създаването на PHP файл с помощни функции или черти.

Въпреки че наследяването на оформлението на шаблона е една от най-мощните функции на Latte, то е ограничено до просто наследяване - един шаблон може да разшири само един друг шаблон. Хоризонталното повторно използване е начин за постигане на многократно наследяване.

Нека имаме набор от дефиниции на блокове:

```latte .{file: blocks.latte}
{block sidebar}...{/block}

{block menu}...{/block}
```

Използвайки командата `{import}`, импортирайте всички блокове и [дефиниции, |#definitions] дефинирани в `blocks.latte`, в друг шаблон:

```latte .{file: child.latte}
{import 'blocks.latte'}

{* вече могат да се използват блокове за странична лента и меню *}
```

Ако импортирате блоковете в родителския шаблон (т.е. използвате `{import}` в `layout.latte`), блоковете ще бъдат достъпни и във всички дъщерни шаблони, което е много удобно.

Шаблонът, който е предназначен да бъде импортиран (напр. `blocks.latte`), не трябва да [разширява |#Layout Inheritance] друг шаблон, т.е. да използвате `{layout}`. Той обаче може да импортира други шаблони.

Тагът `{import}` трябва да е първият таг на шаблона след `{layout}`. Името на шаблона може да бъде произволен израз на PHP:

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

Можете да използвате толкова изрази `{import}`, колкото искате в даден шаблон. Ако два импортирани шаблона дефинират един и същ блок, печели първият. Най-голям приоритет обаче има основният шаблон, който може да презапише всеки импортиран блок.

Всички надписани блокове могат да бъдат включени постепенно, като се вмъкнат като [родителски блок |#Parent-Block]:

```latte
{layout 'layout.latte'}

{import 'blocks.latte'}

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

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

В този пример `{include parent}` правилно ще извика блока `sidebar` от шаблона `blocks.latte`.


Наследяване на блокове `{embed}` .{toc: Unit Inheritance}
=========================================================

Наследяването на единици пренася идеята за наследяване на оформлението на нивото на части от съдържанието. Докато наследяването на оформлението работи със "скелети на документи", които се анимират от подчинени шаблони, наследяването на единици ви позволява да създавате скелети за по-малки части от съдържанието и да ги използвате повторно навсякъде.

При наследяването на единици ключовият етикет е `{embed}`. Той съчетава поведението на `{include}` и `{layout}`. Той ви позволява да включвате съдържание от друг шаблон или блок и по желание да предавате променливи, както и `{include}`. Той също така ви позволява да замените всеки блок, дефиниран в рамките на включен шаблон, както и `{layout}`.

В примера ще използваме сгъваемия елемент акордеон. Нека разгледаме скелета на елемента в шаблона `collapsible.latte`:

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

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

Таговете `{block}` дефинират два блока, които могат да попълват подчинени шаблони. Да, както в случая с шаблона-родител в шаблона за наследяване на оформлението. Можете да видите и променливата `$modifierClass`.

Нека използваме нашия елемент в шаблона. Тук се намира `{embed}`. Това е изключително мощен набор, който ни позволява да правим всичко: да включваме съдържанието на шаблона на елемента, да добавяме променливи към него и да добавяме персонализирани HTML блокове към него:

```latte
{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}
```

Резултатът може да изглежда така:

```latte
<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>
```

Блоковете в таговете за вграждане образуват отделен слой, независим от другите блокове. Затова те могат да имат същото име като блока извън вграждането и не се влияят от него. С помощта на тага [include |#Printing-Blocks] в таговете `{embed}`, можете да вмъкнете блокове, създадени тук, блокове от шаблона за вграждане (които *не са* [локални |#Local-Blocks]), както и блокове от основния шаблон, които *са* локални. Можете също така да [импортирате блокове |#Horizontal-Reuse] от други файлове:

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

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

	{block inner}…{/block}

	{block title}
		{include inner} {* работи, блокът е дефиниран вътре в embed *}
		{include hello} {* работи, блокът е локален в този шаблон *}
		{include content} {* работи, блокът е дефиниран във вградения шаблон *}
		{include aBlockDefinedInImportedTemplate} {* работи *}
		{include outer} {* не работи! - блокът е във външен слой *}
	{/block}
{/embed}
```

Вградените шаблони нямат достъп до активни контекстни променливи, но имат достъп до глобални променливи.

Не само шаблони, но и други блокове могат да бъдат вмъкнати с `{embed}`, така че предишният пример може да бъде написан по следния начин

```latte
{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}
```

Ако подаваме израз на `{embed}` и не е ясно дали става въпрос за блок или име на файл, добавете ключовата дума `block` или `file`:

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


Примери за употреба .[#toc-use-cases]
=====================================

В Latte има различни видове наследяване и повторно използване на кода. Нека обобщим основните понятия за по-голяма яснота:


`{include template}`
--------------------

**Случай на употреба:** Използване на `header.latte` и `footer.latte` вътре в `layout.latte`.

`header.latte`

```latte
<nav>
   <div>Home</div>
   <div>About</div>
</nav>
```

`footer.latte`

```latte
<footer>
   <div>Copyright</div>
</footer>
```

`layout.latte`

```latte
{include 'header.latte'}

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

{include 'footer.latte'}
```


`{layout}`
----------

**Пример за използване**: Разширение `layout.latte` вътре в `homepage.latte` и `about.latte`.

`layout.latte`

```latte
{include 'header.latte'}

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

{include 'footer.latte'}
```

`homepage.latte`

```latte
{layout 'layout.latte'}

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

`about.latte`

```latte
{layout 'layout.latte'}

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


`{import}`
----------

**Приложение за потребителя**: `sidebar.latte` към `single.product.latte` и `single.service.latte`.

`sidebar.latte`

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

`single.product.latte`

```latte
{layout 'product.layout.latte'}

{import 'sidebar.latte'}

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

`single.service.latte`

```latte
{layout 'service.layout.latte'}

{import 'sidebar.latte'}

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


`{define}`
----------

**Пример на потребителя**: Функция, която получава някои променливи и извежда някои маркировки.

`form.latte`

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

`profile.service.latte`

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

**Пример на потребителя**: Вграждане на `pagination.latte` в `product.table.latte` и `service.table.latte`.

`pagination.latte`

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

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

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

Наследяване на шаблони и възможност за повторно използване

Механизмите за повторна употреба и наследяване на шаблони увеличават производителността ви, тъй като всеки шаблон съдържа само уникално съдържание, а повтарящите се елементи и структури се използват повторно. Въвеждаме три концепции: наследяване на оформлението, хоризонтално повторно използване и наследяване на единици.

Концепцията за наследяване на шаблони Latte е подобна на тази за наследяване на класове в PHP. Дефинирате шаблон-родител, от който други наследени шаблони могат да надграждат и да отменят части от шаблона-родител. Това работи чудесно, когато елементите имат обща структура. Звучи сложно? Не се притеснявайте, не е така.

Наследяване на оформлението {layout}

Нека разгледаме наследяването на шаблона на оформлението с пример. Това е родителският шаблон, който в примера ще наречем layout.latte, и той определя HTML скелета на документа.

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

Тагът {block} дефинира три блока, които подчинените шаблони могат да попълват. Единственото, което тагът за блок прави, е да укаже на шаблониращата машина, че подчиненият шаблон може да замени тези части на шаблона, като дефинира свой собствен блок със същото име.

Един шаблон на дете може да изглежда по следния начин:

{layout 'layout.latte'}

{block title}My amazing blog{/block}

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

Ключовият етикет тук е {layout}. Той указва на шаблониращата машина, че този шаблон „разширява“ друг шаблон. Когато Latte визуализира този шаблон, той първо намира родителя – в случая layout.latte.

В този момент механизмът за шаблониране ще забележи три блокови тага в layout.latte и ще замени тези блокове със съдържанието на подчинения шаблон. Имайте предвид, че тъй като шаблонът на детето не е дефинирал блок footer, вместо него се използва съдържанието на шаблона на родителя. Съдържанието в тага {block} в родителския шаблон винаги се използва като резервен вариант.

Изходът може да изглежда по следния начин:

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

В подчинен шаблон блоковете могат да се поставят само на горното ниво или в друг блок, т.е:

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

Освен това блокът винаги ще бъде създаден, независимо дали заобикалящото го условие {if} е оценено като вярно или невярно. Противно на това, което си мислите, този шаблон дефинира блок.

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

Ако искате изходът в блока да се показва условно, използвайте следното:

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

Извънблоковите данни в подчинения шаблон се изпълняват преди шаблонът за оформление да бъде визуализиран, така че можете да го използвате, за да дефинирате променливи от тип {var $foo = bar} и да разпространявате данните по цялата верига на наследяване:

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

...

Наследяване на няколко нива

Можете да използвате толкова нива на наследяване, колкото ви е необходимо. Един от разпространените начини за използване на наследяване на оформлението е следният подход на три нива:

  1. Създайте шаблон layout.latte, в който се съхранява основният външен вид на сайта ви.
  2. Създайте шаблон layout-SECTIONNAME.latte за всеки раздел на сайта си. Например layout-news.latte, layout-blog.latte и т.н. Всички тези шаблони разширяват layout.latte и включват стилове/дизайни за всеки раздел.
  3. Създайте отделни шаблони за всеки тип страница, например новинарска статия или публикация в блог. Тези шаблони разширяват съответния шаблон на раздела.

Динамично наследяване на оформлението

Можете да използвате променлива или какъвто и да е израз на PHP като име на родителския шаблон, така че наследяването да се извършва динамично:

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

Можете също така да използвате приложния програмен интерфейс Latte API за автоматично избиране на шаблона за оформление.

Съвети

Ето няколко съвета за работа с наследяване на оформлението:

  • Ако използвате {layout} в шаблон, той трябва да бъде първият таг на шаблона в този шаблон.
  • Макетът може да се търси автоматично (както в презентаторите). В този случай, ако шаблонът не трябва да има оформление, той ще посочи това с тага {layout none}.
  • Тагът {layout} има псевдоним {extends}.
  • Името на разширения файл на шаблона зависи от програмата за зареждане на шаблони.
  • Можете да имате толкова блокове, колкото искате. Не забравяйте, че не е задължително подчинените шаблони да дефинират всички родителски блокове, така че можете да въведете разумни стойности по подразбиране в няколко блока и след това да дефинирате само тези, които са ви необходими по-късно.

Блокове {block}

Вижте също анонимни {block}

Блокът ви позволява да променяте показването на определена част от шаблона, но не се намесва по никакъв начин в заобикалящата го логика. Нека разгледаме следния пример, за да илюстрираме как работи блокът и, което е по-важно, как не работи:

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

Ако покажете този модел, резултатът е абсолютно същият със или без блокови тагове. Блоковете имат достъп до променливи от външни диапазони. Това е просто начин да се направи така, че да може да се пренастройва за шаблона на детето:

{layout 'parent.Latte'}

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

Сега при визуализиране на шаблона на детето цикълът ще използва блока, дефиниран в шаблона на детето child.Latte, вместо блока, дефиниран в базовия шаблон parent.Latte; изпълненият шаблон ще бъде еквивалентен на следния:

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

Ако обаче създадем нова променлива вътре в именуван блок или заменим стойността на съществуваща променлива, промяната ще бъде видима само вътре в блока:

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

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

Съдържанието на блока може да се променя с помощта на филтри. В следващия пример целият HTML е премахнат и е дадено заглавието:

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

Тагът може да се запише и като n:attribute:

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

Местни блокове

Всеки блок отменя съдържанието на родителския блок със същото име. С изключение на местните блокове. Те донякъде приличат на частните методи в класа. Можете да създадете шаблон, без да се притеснявате, че той ще бъде презаписан от втори шаблон поради съвпадащите имена на блоковете.

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

Блокове за печат {include}

Вижте също {include file}

За да отпечатате блок на определено място, използвайте маркера {include blockname}:

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

<h1>{include title}</h1>

Можете също така да изведете блок от друг шаблон:

{include footer from 'main.latte'}

Производните блокове нямат достъп до променливите на активния контекст, освен ако блокът не е дефиниран в същия файл, в който е включен. Те обаче имат достъп до глобални променливи.

Можете да предавате променливи на блока по следния начин:

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

За име на блока можете да използвате променлива или друг израз в PHP. В този случай добавете ключовата дума block преди променливата, така че по време на компилиране да се знае, че това е блок, а не шаблон за вмъкване, чието име също може да бъде в променливата:

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

Блокът може да бъде отпечатан и вътре в себе си, което е полезно например при визуализиране на дървовидна структура:

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

Вместо {include menu, ...} можете да напишете и {include this, ...}, където this означава текущия блок.

Изходното съдържание може да се променя с помощта на филтри. В следващия пример целият HTML е премахнат и е вмъкнато заглавието:

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

Родителски блок

Ако трябва да визуализирате съдържанието на родителски блок от родителски шаблон, операторът {include parent} е полезен. Това е полезно, ако искате да разширите съдържанието на родителския блок, а не да го замените изцяло.

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

Определения {define}

Освен блокове в Latte има и „дефиниции“. Те могат да се сравнят с функциите в традиционните езици за програмиране. Те са полезни за повторно използване на фрагменти от шаблони, така че да не се повтарят.

Latte се опитва да опрости нещата, така че основно дефинициите са същите като блоковете и всичко, казано за блоковете, се отнася и за дефинициите. Те се различават от блоковете по това:

  1. те са затворени в тагове {define}
  2. визуализират се само когато са вмъкнати чрез {include}
  3. можете да задавате параметри за тях като за функциите в 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> *}

Представете си, че имате помощен шаблон с набор от дефиниции за това как да изготвяте HTML форми.

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

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

Аргументите на дадена дефиниция са винаги незадължителни със стойност по подразбиране null, освен ако не е посочена стойност по подразбиране (тук 'text' е стойността по подразбиране за $type). Типовете параметри също могат да бъдат декларирани: {define input, string $name, ...}.

Шаблонът с дефинициите се зарежда с помощта на {import}. Самите дефиниции се визуализират по същия начин, както блоковете:

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

Дефинициите нямат достъп до променливите на активния контекст, но имат достъп до глобалните променливи.

Динамични имена на блокове

Latte позволява много гъвкаво именуване на блокове, тъй като името на блока може да бъде всеки израз на PHP. В този пример са дефинирани три блока с имена hi-Peter, hi-John и hi-Mary:

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

Например, можем да заменим само един блок в шаблона на детето:

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

Така изходът ще изглежда по следния начин:

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

Проверка на съществуването на блока {ifset}

Вижте също {ifset $var}

Използвайте теста {ifset blockname}, за да проверите дали даден блок (или няколко блока) съществува в текущия контекст:

{ifset footer}
	...
{/ifset}

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

За име на блока можете да използвате променлива или друг израз в PHP. В този случай добавете ключовата дума block пред променливата, за да стане ясно, че тя не е тази, която се тества:

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

Съществуването на блокове се връща и от функцията hasBlock():

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

Съвети

Ето няколко съвета за работа с блокове:

  • Последният блок от най-високо ниво не трябва да има затварящ таг (блокът завършва с края на документа). Това улеснява писането на шаблони за деца с един основен блок.
  • За по-голяма прегледност можете по желание да дадете име на тага {/block}, например {/block footer}. Името обаче трябва да съвпада с името на блока. В по-големи шаблони тази техника помага да се види кои блокови тагове са затворени.
  • Не можете да дефинирате директно няколко блокови тага с едно и също име в един шаблон. Но това може да се постигне чрез използване на динамични имена на блокове.
  • Можете да използвате n:attributes, за да дефинирате блокове като <h1 n:block=title>Welcome to my awesome homepage</h1>
  • Блоковете могат да се използват и без имена, само за да се прилагат филтри към изхода: {block|strip} hello {/block}

Хоризонтална повторна употреба {import}

Хоризонталната повторна употреба е третият механизъм за повторна употреба и наследяване в Latte. Той позволява зареждане на блокове от други шаблони. Това е подобно на създаването на PHP файл с помощни функции или черти.

Въпреки че наследяването на оформлението на шаблона е една от най-мощните функции на Latte, то е ограничено до просто наследяване – един шаблон може да разшири само един друг шаблон. Хоризонталното повторно използване е начин за постигане на многократно наследяване.

Нека имаме набор от дефиниции на блокове:

{block sidebar}...{/block}

{block menu}...{/block}

Използвайки командата {import}, импортирайте всички блокове и дефиниции, дефинирани в blocks.latte, в друг шаблон:

{import 'blocks.latte'}

{* вече могат да се използват блокове за странична лента и меню *}

Ако импортирате блоковете в родителския шаблон (т.е. използвате {import} в layout.latte), блоковете ще бъдат достъпни и във всички дъщерни шаблони, което е много удобно.

Шаблонът, който е предназначен да бъде импортиран (напр. blocks.latte), не трябва да разширява друг шаблон, т.е. да използвате {layout}. Той обаче може да импортира други шаблони.

Тагът {import} трябва да е първият таг на шаблона след {layout}. Името на шаблона може да бъде произволен израз на PHP:

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

Можете да използвате толкова изрази {import}, колкото искате в даден шаблон. Ако два импортирани шаблона дефинират един и същ блок, печели първият. Най-голям приоритет обаче има основният шаблон, който може да презапише всеки импортиран блок.

Всички надписани блокове могат да бъдат включени постепенно, като се вмъкнат като родителски блок:

{layout 'layout.latte'}

{import 'blocks.latte'}

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

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

В този пример {include parent} правилно ще извика блока sidebar от шаблона blocks.latte.

Наследяване на блокове {embed}

Наследяването на единици пренася идеята за наследяване на оформлението на нивото на части от съдържанието. Докато наследяването на оформлението работи със „скелети на документи“, които се анимират от подчинени шаблони, наследяването на единици ви позволява да създавате скелети за по-малки части от съдържанието и да ги използвате повторно навсякъде.

При наследяването на единици ключовият етикет е {embed}. Той съчетава поведението на {include} и {layout}. Той ви позволява да включвате съдържание от друг шаблон или блок и по желание да предавате променливи, както и {include}. Той също така ви позволява да замените всеки блок, дефиниран в рамките на включен шаблон, както и {layout}.

В примера ще използваме сгъваемия елемент акордеон. Нека разгледаме скелета на елемента в шаблона collapsible.latte:

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

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

Таговете {block} дефинират два блока, които могат да попълват подчинени шаблони. Да, както в случая с шаблона-родител в шаблона за наследяване на оформлението. Можете да видите и променливата $modifierClass.

Нека използваме нашия елемент в шаблона. Тук се намира {embed}. Това е изключително мощен набор, който ни позволява да правим всичко: да включваме съдържанието на шаблона на елемента, да добавяме променливи към него и да добавяме персонализирани HTML блокове към него:

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

Резултатът може да изглежда така:

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

Блоковете в таговете за вграждане образуват отделен слой, независим от другите блокове. Затова те могат да имат същото име като блока извън вграждането и не се влияят от него. С помощта на тага include в таговете {embed}, можете да вмъкнете блокове, създадени тук, блокове от шаблона за вграждане (които не са локални), както и блокове от основния шаблон, които са локални. Можете също така да импортирате блокове от други файлове:

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

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

	{block inner}…{/block}

	{block title}
		{include inner} {* работи, блокът е дефиниран вътре в embed *}
		{include hello} {* работи, блокът е локален в този шаблон *}
		{include content} {* работи, блокът е дефиниран във вградения шаблон *}
		{include aBlockDefinedInImportedTemplate} {* работи *}
		{include outer} {* не работи! - блокът е във външен слой *}
	{/block}
{/embed}

Вградените шаблони нямат достъп до активни контекстни променливи, но имат достъп до глобални променливи.

Не само шаблони, но и други блокове могат да бъдат вмъкнати с {embed}, така че предишният пример може да бъде написан по следния начин

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

Ако подаваме израз на {embed} и не е ясно дали става въпрос за блок или име на файл, добавете ключовата дума block или file:

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

Примери за употреба

В Latte има различни видове наследяване и повторно използване на кода. Нека обобщим основните понятия за по-голяма яснота:

{include template}

Случай на употреба: Използване на header.latte и footer.latte вътре в 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}

Пример за използване: Разширение layout.latte вътре в 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}

Приложение за потребителя: sidebar.latte към 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}

Пример на потребителя: Функция, която получава някои променливи и извежда някои маркировки.

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}

Пример на потребителя: Вграждане на pagination.latte в 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}