Nette Documentation Preview

syntax
Latte pro vývojáře
******************


Jak vykreslit šablonu
=====================

Jak vykreslit šablonu? Stačí spustit tento kód:

```php
$latte = new Latte\Engine;

$latte->setTempDirectory('/path/to/tempdir');

$params = [
	'items' => ['one', 'two', 'three'],
];

// kresli na výstup
$latte->render('template.latte', $params);
// kresli do řetězce
$html = $latte->renderToString('template.latte', $params);
```

Latte automaticky přegenerovává cache při každé změně šablony, což můžeme v produkčním prostředí vypnout a ušetřit tím malinko výkonu:

```php
$latte->setAutoRefresh(false);
```


Typově bezpečné parametry
-------------------------

Místo pole `$params` můžete také použít objekt, což přináší některé výhody. Získáte typově bezpečný zápis, [příjemné napovídání v IDE|type-system]
a cestu pro [registraci filtrů|#Vlastní filtr] a [funkcí|#Funkce]. Příklad zápisu v PHP 8:

```php
class MailTemplate
{
	public function __construct(
		public string $lang = 'cs',
		public Address $address,
		public string $subject,
		public array $items,
		public ?float $price = null,
	) {}
}

$latte->render('mail.latte', new MailTemplate(
	lang: $this->lang,
	subject: $title,
	price: $this->getPrice(),
	items: [],
	address: $userAddress,
));
```


Načítání šablon z řetězce
=========================

Pokud nemáme naši šablonu uloženou v souboru, ale pouze v proměnných, musíme nastavit loader na `Latte\Loaders\StringLoader`.

```php
$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file', $params);
```


Vlastní filtr
=============

Jako filtr lze do šablony zaregistrovat libovolný callback:

```php
$latte = new Latte\Engine;
$latte->addFilter('shortify', function (string $s): string {
	return mb_substr($s, 0, 10); // zkrátí text na 10 písmen
});
```

V tomto případě by bylo šikovnější, kdyby filtr přijímal další parametr:

```php
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});
```

V šabloně se potom volá takto:

```html
<p>{$text|shortify}</p>
<p>{$text|shortify:100}</p>
```

Druhým způsobem definice vlastního filtru je třída šablony. Vytvoříte metodu a uvedete u ní atribut `TemplateFilter`:

```php
class MyTemplate
{
	#[Latte\Attributes\TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len); // zkrátí text na 10 písmen
	}
}

$params = new MyTemplate;
$latte->render('template.latte', $params);
```

Pokud používáte PHP starší než 8.0 a Latte 2.x, můžete místo atributu použít anotaci:

```php
/** @filter */
public function shortify(string $s, int $len = 10): string
{
	return mb_substr($s, 0, $len);
}
```


Zavaděč filtrů .{data-version:2.10}
-----------------------------------

Ruční registraci více filtrů lze nahradit jedním zavaděčem filtrů:

```php
$latte->addFilterLoader([new Filters, 'loader']);
```

Metoda `loader()` získá název filtru a vrátí callable:

```php
class Filters
{
	public function loader(string $filter): ?callable
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter];
		}
		return null;
	}

	public function shortify($s, $len = 10)
	{
		return mb_substr($s, 0, $len);
	}

	// ...
}
```


Universal filter
----------------

Před Latte v2.10 se místo zavaděče filtrů používal tento způsob:

```php
$latte->addFilter(null, [new Filters, 'loader']);

class Filters
{
	public function loader(string $filter, ...$args)
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter](...$args);
		}
	}

	// ...
}
```


Escapování
----------

Řetězec, který filtr přijímá na vstupu a vrací na výstupu, je standardně plain text v UTF‑8. V případě, že filtr vrací HTML, zabalí řetězec do objektu `Latte\Runtime\Html`, aby nedošlo k nežádoucímu automatickému escapování v šabloně:

```php
$latte->addFilter('money', function (int $num): string {
	return new Latte\Runtime\Html("<i>$num Kč</i>");
});
```

.[note]
Filtr musí zajistit správné escapování dat.


Blokové filtry
--------------

Filtry, které se používají na bloky (např. [anonymní bloky|tags#block]) dostávají informaci o typu obsahu a kontextu v objektu [api:Latte\Runtime\FilterInfo], který se předává jako první parameter. Filtr by měl nejprve ověřit, že daný content type podporuje:

```php
use Latte\Engine;
use Latte\Runtime\FilterInfo;

$latte->addFilter('tidyHtml', function (FilterInfo $info, string $s): string {
	// ověříme, že v $s je HTML text
	if ($info->contentType !== Engine::CONTENT_HTML) {
		throw new Exception('Filter |tidyHtml used in incompatible content type.');
	}
	// ...
});
```

U těch filtrů se nepoužívá zabalení do objektu `Latte\Runtime\Html`, informace o typu se vrací v objektu `$info`. Tj. pokud vrací data v jiném typu, změní hodnotu v `$info`:

```php
$info->contentType = Engine::CONTENT_TEXT;
```

Rozlišují se následující typy a jejich kontexty:

```php
use Latte\Engine;
use Latte\Compiler;

Engine::CONTENT_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TAG
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_ATTRIBUTE
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_COMMENT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_CSS
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_JS
Engine::CONTENT_XHTML
Engine::CONTENT_XML
Engine::CONTENT_JS
Engine::CONTENT_CSS
Engine::CONTENT_ICAL
```

Pokud se blokový filtr použije běžným způsobem (tj. `{$foo|tidyHtml}`), obsahuje `$info->contentType` hodnotu null.


Funkce
======

V Latte můžete používat všechny funkce PHP a zároveň si definovat své vlastní:

```php
$latte = new Latte\Engine;
$latte->addFunction('random', function (...$args) {
	return $args[array_rand($args)];
});
```

Použití je pak stejné, jako když voláte PHP funkci:

```html
{random(jablko, pomeranč, citron)} // vypíše například: jablko
```

Druhým způsobem definice vlastní funkce je třída šablony. Vytvoříte metodu a uvedete u ní atribut `TemplateFunction`:

```php
class MyTemplate
{
	#[Latte\Attributes\TemplateFunction]
	public function random(...$args)
	{
		return $args[array_rand($args)];
	}
}

$params = new MyTemplate;
$latte->render('template.latte', $params);
```

Pokud používáte PHP starší než 8.0 a Latte 2.x, můžete místo atributu použít anotaci:

```php
/** @function */
public function random(...$args)
{
	return $args[array_rand($args)];
}
```


Vlastní tagy
============

Latte poskytuje API pro tvorbu vlastních tagů. Není to nic složitého. Značky přidáváme v sadách, přičemž sadu může tvořit i jediná značka.

```php
$latte = new Latte\Engine;

// vytvoříme si sadu
$set = new Latte\Macros\MacroSet($latte->getCompiler());

// do sady přidáme párové tagy {try} ... {/try}
$set->addMacro(
	'try', // název tagu
	'try {',  // PHP kód nahrazující otevírací tag
	'} catch (\Exception $e) {}' // kód nahrazující uzavírací tag
);
```

Pokud značka není párová, třetí parametr metody `addMacro()` vynecháme.

PHP kód uváděný ve druhém a třetím parametru může obsahovat zástupné symboly:

- `%node.word` - vloží první argument tagu
- `%node.array` - vloží argumenty tagu naformátované jako PHP pole
- `%node.args` - vloží argumenty tagu naformátované jako PHP kód
- `%node.line` - vloží komentář s číslem řádku v šabloně
- `%escape(...)` - nahradí za aktuální escapovací funkci
- `%modify(...)` - nahradí sérií filtrovacích funkcí

Příklad:

```php
$set->addMacro('if', 'if (%node.args):', 'endif');
```

Pokud je logika značek ještě složitější, můžeme místo řetězců uvést callbacky či lambda funkce. Jako první parametr dostanou objekt [MacroNode |api:Latte\MacroNode] reprezentující aktuální uzel, druhým parametrem je objekt [PhpWriter |api:Latte\PhpWriter], který usnadňuje generování výstupního kódu.

```php
$set->addMacro('if', function ($node, $writer) {
	return $writer->write('if (%node.args):');
}, 'endif');
```


Exception handler
=================

Můžete si definovat vlastní obslužný handler pro očekávané výjimky. Předají se mu výjimky vzniklé uvnitř [`{try}`|tags#try] a v [sandboxu|sandbox].

```php
$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
	$logger->log($e);
};

$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);
```


Dohledávání layoutu
===================

Rozhraní API můžete použít k výběru šablony layout, která se má použít, když podřízená šablona neobsahuje značku `{layout}`. To zjednoduší psaní šablon nebo umožní automatické dohledávání layoutů.

Toho se dosáhne následujícím způsobem:

```php
$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// vrací cestu k souboru s layoutem
		return 'automatic.layout.latte';
	}
};

$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);
```

/--comment


Loadery
=======

TODO


Validating the Template Syntax
==============================


Using a Database to store Templates
===================================


Using different Template Sources
================================


Loading a Template from a String
================================


Latte\Runtime\Html, Latte\HtmlStringable
========================================
\--


{{composer: latte/latte}}

Latte pro vývojáře

Jak vykreslit šablonu

Jak vykreslit šablonu? Stačí spustit tento kód:

$latte = new Latte\Engine;

$latte->setTempDirectory('/path/to/tempdir');

$params = [
	'items' => ['one', 'two', 'three'],
];

// kresli na výstup
$latte->render('template.latte', $params);
// kresli do řetězce
$html = $latte->renderToString('template.latte', $params);

Latte automaticky přegenerovává cache při každé změně šablony, což můžeme v produkčním prostředí vypnout a ušetřit tím malinko výkonu:

$latte->setAutoRefresh(false);

Typově bezpečné parametry

Místo pole $params můžete také použít objekt, což přináší některé výhody. Získáte typově bezpečný zápis, příjemné napovídání v IDE a cestu pro registraci filtrů a funkcí. Příklad zápisu v PHP 8:

class MailTemplate
{
	public function __construct(
		public string $lang = 'cs',
		public Address $address,
		public string $subject,
		public array $items,
		public ?float $price = null,
	) {}
}

$latte->render('mail.latte', new MailTemplate(
	lang: $this->lang,
	subject: $title,
	price: $this->getPrice(),
	items: [],
	address: $userAddress,
));

Načítání šablon z řetězce

Pokud nemáme naši šablonu uloženou v souboru, ale pouze v proměnných, musíme nastavit loader na Latte\Loaders\StringLoader.

$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file', $params);

Vlastní filtr

Jako filtr lze do šablony zaregistrovat libovolný callback:

$latte = new Latte\Engine;
$latte->addFilter('shortify', function (string $s): string {
	return mb_substr($s, 0, 10); // zkrátí text na 10 písmen
});

V tomto případě by bylo šikovnější, kdyby filtr přijímal další parametr:

$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

V šabloně se potom volá takto:

<p>{$text|shortify}</p>
<p>{$text|shortify:100}</p>

Druhým způsobem definice vlastního filtru je třída šablony. Vytvoříte metodu a uvedete u ní atribut TemplateFilter:

class MyTemplate
{
	#[Latte\Attributes\TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len); // zkrátí text na 10 písmen
	}
}

$params = new MyTemplate;
$latte->render('template.latte', $params);

Pokud používáte PHP starší než 8.0 a Latte 2.x, můžete místo atributu použít anotaci:

/** @filter */
public function shortify(string $s, int $len = 10): string
{
	return mb_substr($s, 0, $len);
}

Zavaděč filtrů

Ruční registraci více filtrů lze nahradit jedním zavaděčem filtrů:

$latte->addFilterLoader([new Filters, 'loader']);

Metoda loader() získá název filtru a vrátí callable:

class Filters
{
	public function loader(string $filter): ?callable
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter];
		}
		return null;
	}

	public function shortify($s, $len = 10)
	{
		return mb_substr($s, 0, $len);
	}

	// ...
}

Universal filter

Před Latte v2.10 se místo zavaděče filtrů používal tento způsob:

$latte->addFilter(null, [new Filters, 'loader']);

class Filters
{
	public function loader(string $filter, ...$args)
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter](...$args);
		}
	}

	// ...
}

Escapování

Řetězec, který filtr přijímá na vstupu a vrací na výstupu, je standardně plain text v UTF‑8. V případě, že filtr vrací HTML, zabalí řetězec do objektu Latte\Runtime\Html, aby nedošlo k nežádoucímu automatickému escapování v šabloně:

$latte->addFilter('money', function (int $num): string {
	return new Latte\Runtime\Html("<i>$num Kč</i>");
});

Filtr musí zajistit správné escapování dat.

Blokové filtry

Filtry, které se používají na bloky (např. anonymní bloky) dostávají informaci o typu obsahu a kontextu v objektu Latte\Runtime\FilterInfo, který se předává jako první parameter. Filtr by měl nejprve ověřit, že daný content type podporuje:

use Latte\Engine;
use Latte\Runtime\FilterInfo;

$latte->addFilter('tidyHtml', function (FilterInfo $info, string $s): string {
	// ověříme, že v $s je HTML text
	if ($info->contentType !== Engine::CONTENT_HTML) {
		throw new Exception('Filter |tidyHtml used in incompatible content type.');
	}
	// ...
});

U těch filtrů se nepoužívá zabalení do objektu Latte\Runtime\Html, informace o typu se vrací v objektu $info. Tj. pokud vrací data v jiném typu, změní hodnotu v $info:

$info->contentType = Engine::CONTENT_TEXT;

Rozlišují se následující typy a jejich kontexty:

use Latte\Engine;
use Latte\Compiler;

Engine::CONTENT_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TAG
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_ATTRIBUTE
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_COMMENT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_CSS
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_JS
Engine::CONTENT_XHTML
Engine::CONTENT_XML
Engine::CONTENT_JS
Engine::CONTENT_CSS
Engine::CONTENT_ICAL

Pokud se blokový filtr použije běžným způsobem (tj. {$foo|tidyHtml}), obsahuje $info->contentType hodnotu null.

Funkce

V Latte můžete používat všechny funkce PHP a zároveň si definovat své vlastní:

$latte = new Latte\Engine;
$latte->addFunction('random', function (...$args) {
	return $args[array_rand($args)];
});

Použití je pak stejné, jako když voláte PHP funkci:

{random(jablko, pomeranč, citron)} // vypíše například: jablko

Druhým způsobem definice vlastní funkce je třída šablony. Vytvoříte metodu a uvedete u ní atribut TemplateFunction:

class MyTemplate
{
	#[Latte\Attributes\TemplateFunction]
	public function random(...$args)
	{
		return $args[array_rand($args)];
	}
}

$params = new MyTemplate;
$latte->render('template.latte', $params);

Pokud používáte PHP starší než 8.0 a Latte 2.x, můžete místo atributu použít anotaci:

/** @function */
public function random(...$args)
{
	return $args[array_rand($args)];
}

Vlastní tagy

Latte poskytuje API pro tvorbu vlastních tagů. Není to nic složitého. Značky přidáváme v sadách, přičemž sadu může tvořit i jediná značka.

$latte = new Latte\Engine;

// vytvoříme si sadu
$set = new Latte\Macros\MacroSet($latte->getCompiler());

// do sady přidáme párové tagy {try} ... {/try}
$set->addMacro(
	'try', // název tagu
	'try {',  // PHP kód nahrazující otevírací tag
	'} catch (\Exception $e) {}' // kód nahrazující uzavírací tag
);

Pokud značka není párová, třetí parametr metody addMacro() vynecháme.

PHP kód uváděný ve druhém a třetím parametru může obsahovat zástupné symboly:

  • %node.word – vloží první argument tagu
  • %node.array – vloží argumenty tagu naformátované jako PHP pole
  • %node.args – vloží argumenty tagu naformátované jako PHP kód
  • %node.line – vloží komentář s číslem řádku v šabloně
  • %escape(...) – nahradí za aktuální escapovací funkci
  • %modify(...) – nahradí sérií filtrovacích funkcí

Příklad:

$set->addMacro('if', 'if (%node.args):', 'endif');

Pokud je logika značek ještě složitější, můžeme místo řetězců uvést callbacky či lambda funkce. Jako první parametr dostanou objekt MacroNode reprezentující aktuální uzel, druhým parametrem je objekt PhpWriter, který usnadňuje generování výstupního kódu.

$set->addMacro('if', function ($node, $writer) {
	return $writer->write('if (%node.args):');
}, 'endif');

Exception handler

Můžete si definovat vlastní obslužný handler pro očekávané výjimky. Předají se mu výjimky vzniklé uvnitř {try} a v sandboxu.

$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
	$logger->log($e);
};

$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);

Dohledávání layoutu

Rozhraní API můžete použít k výběru šablony layout, která se má použít, když podřízená šablona neobsahuje značku {layout}. To zjednoduší psaní šablon nebo umožní automatické dohledávání layoutů.

Toho se dosáhne následujícím způsobem:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// vrací cestu k souboru s layoutem
		return 'automatic.layout.latte';
	}
};

$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);