Skip to content

Commit 1422f06

Browse files
committed
application: improved info about template variables and extensions
1 parent b456906 commit 1422f06

File tree

2 files changed

+174
-60
lines changed

2 files changed

+174
-60
lines changed

application/cs/templates.texy

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,34 @@ Soubory, kde se dohledávají šablony layoutu, lze změnit překrytím metody [
114114
Proměnné v šabloně
115115
------------------
116116

117-
Proměnné do šablony předáváme tak, že je zapíšeme do `$this->template` a potom je máme k dispozici v šabloně jako lokální proměnné:
117+
Proměnné do šablony předáváme zápisem do `$this->template`. V šabloně jsou pak dostupné jako lokální proměnné:
118118

119119
```php
120120
$this->template->article = $this->articles->getById($id);
121121
```
122122

123-
Takto jednoduše můžeme do šablon předat jakékoliv proměnné. Při vývoji robustních aplikací ale bývá užitečnější se omezit. Například tak, že explicitně nadefinujeme výčet proměnných, které šablona očekává, a jejich typů. Díky tomu nám bude moci PHP kontrolovat typy, IDE správně našeptávat a statická analýza odhalovat chyby.
124123

125-
A jak takový výčet nadefinujeme? Jednoduše v podobě třídy a její properties. Pojmenujeme ji podobně jako presenter, jen s `Template` na konci:
124+
Výchozí proměnné
125+
----------------
126+
127+
Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky:
128+
129+
- `$basePath` je absolutní URL cesta ke kořenovému adresáři (např. `/eshop`)
130+
- `$baseUrl` je absolutní URL ke kořenovému adresáři (např. `http://localhost/eshop`)
131+
- `$user` je objekt [reprezentující uživatele |security:authentication]
132+
- `$presenter` je aktuální presenter
133+
- `$control` je aktuální komponenta nebo presenter
134+
- `$flashes` pole [zpráv |presenters#Flash zprávy] zaslaných funkcí `flashMessage()`
135+
136+
Pokud používáte vlastní třídu šablony, tyto proměnné se předají, pokud pro ně vytvoříte property.
137+
138+
139+
Typově bezpečné šablony
140+
-----------------------
141+
142+
Při vývoji robustních aplikací je užitečné explicitně nadefinovat, jaké proměnné šablona očekává a jakého jsou typu. Získáte tak typovou kontrolu v PHP, chytré našeptávání v IDE a schopnost statické analýzy odhalovat chyby.
143+
144+
Jak takový výčet nadefinovat? Jednoduše v podobě třídy s properties reprezentujícími proměnné šablony. Pojmenujeme ji podobně jako presenter, jen s `Template` na konci:
126145

127146
```php
128147
/**
@@ -141,22 +160,22 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
141160
}
142161
```
143162

144-
Objekt `$this->template` v presenteru bude nyní instancí třídy `ArticleTemplate`. Takže PHP bude při zápisu kontrolovat deklarované typy. A počínaje verzí PHP 8.2 upozorní i na zápis do neexistující proměnné, v předchozích verzích lze téhož dosáhnout použitím traity [Nette\SmartObject |utils:smartobject].
163+
Objekt `$this->template` v presenteru bude nyní instancí třídy `ArticleTemplate`. PHP tak bude při zápisu kontrolovat deklarované typy.
145164

146-
Anotace `@property-read` je určená pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.
165+
Anotace `@property-read` slouží pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.
147166

148167
[* phpstorm-completion.webp *]
149168

150-
Luxusu našeptávání si můžete dopřát i v šablonách, stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy, více v článku "Latte: jak na typový systém":https://blog.nette.org/cs/latte-jak-na-typovy-system:
169+
Našeptávání můžete využít i přímo v šablonách. Stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy parametrů šablony, více v článku "Latte: jak na typový systém":https://blog.nette.org/cs/latte-jak-na-typovy-system:
151170

152171
```latte
153172
{templateType App\Presentation\Article\ArticleTemplate}
154173
...
155174
```
156175

157-
Takto fungují i šablony v komponentách, stačí jen dodržet jmennou konvenci a pro komponentu např. `FifteenControl` vytvořit třídu šablony `FifteenTemplate`.
176+
Totéž platí i pro komponenty. Stačí dodržet jmennou konvenci a pro komponentu např. `FifteenControl` vytvořit třídu parametrů `FifteenTemplate`.
158177

159-
Pokud potřebujete vytvořit `$template` jako instanci jiné třídy, využijte metodu `createTemplate()`:
178+
Pokud potřebujete použít jinou třídu parametrů, využijte metodu `createTemplate()`:
160179

161180
```php
162181
public function renderDefault(): void
@@ -169,21 +188,6 @@ public function renderDefault(): void
169188
```
170189

171190

172-
Výchozí proměnné
173-
----------------
174-
175-
Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky:
176-
177-
- `$basePath` je absolutní URL cesta ke kořenovému adresáři (např. `/eshop`)
178-
- `$baseUrl` je absolutní URL ke kořenovému adresáři (např. `http://localhost/eshop`)
179-
- `$user` je objekt [reprezentující uživatele |security:authentication]
180-
- `$presenter` je aktuální presenter
181-
- `$control` je aktuální komponenta nebo presenter
182-
- `$flashes` pole [zpráv |presenters#Flash zprávy] zaslaných funkcí `flashMessage()`
183-
184-
Pokud používáte vlastní třídu šablony, tyto proměnné se předají, pokud pro ně vytvoříte property.
185-
186-
187191
Vytváření odkazů
188192
----------------
189193

@@ -205,21 +209,67 @@ Více informací najdete v kapitole [Vytváření odkazů URL|creating-links].
205209
Vlastní filtry, značky apod.
206210
----------------------------
207211

208-
Šablonovací systém Latte lze rozšířit o vlastní filtry, funkce, značky apod. Lze tak učinit přímo v metodě `render<View>` nebo `beforeRender()`:
212+
Šablonovací systém Latte lze rozšířit o vlastní filtry, funkce, značky a další prvky. K dispozici jsou tři způsoby, jak to udělat, od nejrychlejších ad-hoc řešení až po architektonický přístup pro celou aplikaci.
213+
214+
**Ad-hoc v metodách presenteru**
215+
216+
Nejrychlejší způsob je přidat filtr nebo funkci přímo v kódu presenteru či komponenty. V presenteru je k tomu vhodná metoda `beforeRender()` nebo `render<View>()`:
209217

210218
```php
211-
public function beforeRender(): void
219+
protected function beforeRender(): void
212220
{
213221
// přidání filtru
214-
$this->template->addFilter('foo', /* ... */);
222+
$this->template->addFilter('money', fn($val) => round($val) . ' Kč');
223+
224+
// přidání funkce
225+
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
226+
}
227+
```
215228

216-
// nebo konfigurujeme přímo objekt Latte\Engine
229+
V šabloně pak:
230+
231+
```latte
232+
<p>Cena: {$price|money}</p>
233+
234+
{if isWeekend($now)} ... {/if}
235+
```
236+
237+
Pro složitější logiku můžete konfigurovat přímo objekt `Latte\Engine`:
238+
239+
```php
240+
protected function beforeRender(): void
241+
{
217242
$latte = $this->template->getLatte();
218243
$latte->setMigrationWarnings();
219244
}
220245
```
221246

222-
Latte ve verzi 3 nabízí pokročilejší způsob a to vytvoření si [extension |latte:extending-latte#Latte Extension] pro každý webový projekt. Kusý příklad takové třídy:
247+
**Pomocí atributů**
248+
249+
Elegantní způsob je definovat filtry a funkce jako metody přímo ve [třídě parametrů šablony|#Typově bezpečné šablony] presenteru nebo komponenty a označit je atributy:
250+
251+
```php
252+
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
253+
{
254+
#[Latte\Attributes\TemplateFilter]
255+
public function money(float $val): string
256+
{
257+
return round($val) . ' Kč';
258+
}
259+
260+
#[Latte\Attributes\TemplateFunction]
261+
public function isWeekend(DateTimeInterface $date): bool
262+
{
263+
return $date->format('N') >= 6
264+
}
265+
}
266+
```
267+
268+
Latte automaticky rozpozná a zaregistruje metody označené těmito atributy. Název filtru nebo funkce v šabloně odpovídá názvu metody. Tyto metody nesmí být privátní.
269+
270+
**Globálně pomocí Extension**
271+
272+
Předchozí způsoby jsou vhodné pro filtry a funkce, které potřebujete jen v konkrétním presenteru nebo komponentě, nikoliv v celé aplikaci. Pro celou aplikaci je nejvhodnější vytvořit si [extension |latte:extending-latte#Latte Extension]. Jde o třídu, která centralizuje všechna rozšíření Latte pro celý projekt. Kusý příklad:
223273

224274
```php
225275
namespace App\Presentation\Accessory;
@@ -251,18 +301,25 @@ final class LatteExtension extends Latte\Extension
251301
];
252302
}
253303

304+
private function filterTimeAgoInWords(DateTimeInterface $time): string
305+
{
306+
// ...
307+
}
308+
254309
// ...
255310
}
256311
```
257312

258-
Zaregistrujeme ji pomocí [konfigurace |configuration#Šablony Latte]:
313+
Extension zaregistrujeme pomocí [konfigurace |configuration#Šablony Latte]:
259314

260315
```neon
261316
latte:
262317
extensions:
263318
- App\Presentation\Accessory\LatteExtension
264319
```
265320

321+
Výhodou extension je, že lze využít dependency injection, mít přístup k modelové vrstvě aplikace a všechna rozšíření mít přehledně na jednom místě. Extension umožnuje definovat i vlastní značky, providery, průchody pro Latte kompilátor a další.
322+
266323

267324
Překládání
268325
----------

application/en/templates.texy

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,37 @@ Using `$this->setLayout(false)` or the `{layout none}` tag inside the template d
111111
The files where layout templates are looked up can be changed by overriding the [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] method, which returns an array of possible file names.
112112

113113

114-
Variables in the Template
115-
-------------------------
114+
Template Variables
115+
------------------
116116

117-
Variables are passed to the template by writing them to `$this->template`, and then they are available in the template as local variables:
117+
Variables are passed to templates by writing them to `$this->template`. They then become available in the template as local variables:
118118

119119
```php
120120
$this->template->article = $this->articles->getById($id);
121121
```
122122

123-
This way, we can easily pass any variables to templates. However, when developing robust applications, it is often more useful to impose limitations. For example, by explicitly defining a list of variables that the template expects and their types. This allows PHP to perform type checking, the IDE to provide correct autocompletion, and static analysis to detect errors.
124123

125-
And how do we define such a list? Simply in the form of a class and its properties. We name it similarly to the presenter, but with `Template` at the end:
124+
Default Variables
125+
-----------------
126+
127+
Presenters and components automatically pass several useful variables to templates:
128+
129+
- `$basePath` is the absolute URL path to the root directory (e.g., `/eshop`)
130+
- `$baseUrl` is the absolute URL to the root directory (e.g., `http://localhost/eshop`)
131+
- `$user` is an object [representing the user |security:authentication]
132+
- `$presenter` is the current presenter
133+
- `$control` is the current component or presenter
134+
- `$flashes` is an array of [messages |presenters#Flash Messages] sent by the `flashMessage()` function
135+
136+
If you use a custom template class, these variables are passed if you create a property for them.
137+
138+
139+
Type-Safe Templates
140+
-------------------
141+
142+
When developing robust applications, it's useful to explicitly define which variables the template expects and their types. This provides type checking in PHP, smart hints in your IDE, and enables static analysis to catch errors.
143+
144+
How do you define such a list? Simply as a class with properties representing template variables. Name it similarly to the presenter, just with `Template` at the end:
126145

127146
```php
128147
/**
@@ -141,22 +160,22 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
141160
}
142161
```
143162

144-
The `$this->template` object in the presenter will now be an instance of the `ArticleTemplate` class. So PHP will check the declared types upon writing. And starting from PHP 8.2, it will also warn about writing to a non-existent variable; in previous versions, the same can be achieved using the [Nette\SmartObject |utils:smartobject] trait.
163+
The `$this->template` object in the presenter will now be an instance of the `ArticleTemplate` class. PHP will thus check the declared types when writing.
145164

146-
The `@property-read` annotation is intended for IDEs and static analysis; thanks to it, autocompletion will work, see "PhpStorm and code completion for $this->template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.
165+
The `@property-read` annotation is for the IDE and static analysis, enabling code completion, see "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.
147166

148167
[* phpstorm-completion.webp *]
149168

150-
You can enjoy the luxury of autocompletion in templates too; just install the Latte plugin for PhpStorm and specify the class name at the beginning of the template, more in the article "Latte: How to Use Type System":https://blog.nette.org/en/latte-how-to-use-type-system:
169+
You can also use code completion directly in templates. Just install the Latte plugin for PhpStorm and specify the template parameter class name at the beginning of the template, more in the article "Latte: how to use the type system":https://blog.nette.org/en/latte-how-to-use-type-system:
151170

152171
```latte
153172
{templateType App\Presentation\Article\ArticleTemplate}
154173
...
155174
```
156175

157-
This is also how templates work in components; just follow the naming convention and create a template class `FifteenTemplate` for a component like `FifteenControl`.
176+
The same applies to components. Just follow the naming convention and create a parameter class `FifteenTemplate` for a component like `FifteenControl`.
158177

159-
If you need to create `$template` as an instance of another class, use the `createTemplate()` method:
178+
If you need to use a different parameter class, use the `createTemplate()` method:
160179

161180
```php
162181
public function renderDefault(): void
@@ -169,21 +188,6 @@ public function renderDefault(): void
169188
```
170189

171190

172-
Default Variables
173-
-----------------
174-
175-
Presenters and components automatically pass several useful variables to templates:
176-
177-
- `$basePath` is the absolute URL path to the root directory (e.g., `/eshop`)
178-
- `$baseUrl` is the absolute URL to the root directory (e.g., `http://localhost/eshop`)
179-
- `$user` is an object [representing the user |security:authentication]
180-
- `$presenter` is the current presenter
181-
- `$control` is the current component or presenter
182-
- `$flashes` is an array of [messages |presenters#Flash Messages] sent by the `flashMessage()` function
183-
184-
If you use a custom template class, these variables are passed if you create a property for them.
185-
186-
187191
Creating Links
188192
--------------
189193

@@ -205,21 +209,67 @@ More information can be found in the chapter [Creating URL Links|creating-links]
205209
Custom Filters, Tags, etc.
206210
--------------------------
207211

208-
The Latte templating system can be extended with custom filters, functions, tags, etc. This can be done directly in the `render<View>` or `beforeRender()` method:
212+
The Latte templating system can be extended with custom filters, functions, tags, and other elements. There are three approaches available, ranging from quick ad-hoc solutions to architectural patterns for entire applications.
213+
214+
**Ad-hoc in Presenter Methods**
215+
216+
The quickest approach is adding filters or functions directly in presenter or component code. In presenters, the `beforeRender()` or `render<View>()` methods work well for this:
209217

210218
```php
211-
public function beforeRender(): void
219+
protected function beforeRender(): void
212220
{
213221
// adding a filter
214-
$this->template->addFilter('foo', /* ... */);
222+
$this->template->addFilter('money', fn($val) => '$' . number_format($val, 2));
223+
224+
// adding a function
225+
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
226+
}
227+
```
228+
229+
In the template:
230+
231+
```latte
232+
<p>Price: {$price|money}</p>
233+
234+
{if isWeekend($now)} ... {/if}
235+
```
215236

216-
// or configure the Latte\Engine object directly
237+
For more complex logic, you can configure the `Latte\Engine` object directly:
238+
239+
```php
240+
protected function beforeRender(): void
241+
{
217242
$latte = $this->template->getLatte();
218243
$latte->setMigrationWarnings();
219244
}
220245
```
221246

222-
Latte version 3 offers a more advanced way by creating an [extension |latte:extending-latte#Latte Extension] for each web project. Here is a brief example of such a class:
247+
**Using Attributes**
248+
249+
A more elegant approach is defining filters and functions as methods directly in the presenter or component's [template parameter class|#Type-safe templates], marked with attributes:
250+
251+
```php
252+
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
253+
{
254+
#[Latte\Attributes\TemplateFilter]
255+
public function money(float $val): string
256+
{
257+
return '$' . number_format($val, 2);
258+
}
259+
260+
#[Latte\Attributes\TemplateFunction]
261+
public function isWeekend(DateTimeInterface $date): bool
262+
{
263+
return $date->format('N') >= 6
264+
}
265+
}
266+
```
267+
268+
Latte automatically discovers and registers methods marked with these attributes. The filter or function name in templates matches the method name. These methods must not be private.
269+
270+
**Globally Using Extensions**
271+
272+
The previous approaches suit filters and functions needed only in specific presenters or components, not application-wide. For the entire application, creating an [extension |latte:extending-latte#Latte Extension] works best. This class centralizes all Latte extensions for your project. A brief example:
223273

224274
```php
225275
namespace App\Presentation\Accessory;
@@ -251,18 +301,25 @@ final class LatteExtension extends Latte\Extension
251301
];
252302
}
253303

304+
private function filterTimeAgoInWords(DateTimeInterface $time): string
305+
{
306+
// ...
307+
}
308+
254309
// ...
255310
}
256311
```
257312

258-
We register it using [configuration |configuration#Latte Templates]:
313+
Register the extension through [configuration |configuration#Latte Templates]:
259314

260315
```neon
261316
latte:
262317
extensions:
263318
- App\Presentation\Accessory\LatteExtension
264319
```
265320

321+
Extensions offer several advantages: dependency injection support, access to your application's model layer, and centralized management of all extensions. They also support custom tags, providers, compiler passes, and more.
322+
266323

267324
Translating
268325
-----------

0 commit comments

Comments
 (0)