Nette Documentation Preview

syntax
Sablonok
********

.[perex]
A Nette a [Latte |latte:] sablonrendszert használja. Egyrészt azért, mert ez a legbiztonságosabb sablonrendszer PHP-hoz, másrészt pedig a legintuitívabb rendszer. Nem kell sok újat tanulnia, elegendő a PHP ismerete és néhány tag.

Gyakori, hogy egy oldal egy layout sablonból + az adott akció sablonjából áll össze. Így nézhet ki például egy layout sablon, figyelje meg a `{block}` blokkokat és a `{include}` taget:

```latte
<!DOCTYPE html>
<html>
<head>
	<title>{block title}Saját Alkalmazás{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>
```

És ez lesz az akció sablonja:

```latte
{block title}Kezdőlap{/block}

{block content}
<h1>Kezdőlap</h1>
...
{/block}
```

Ez definiálja a `content` blokkot, amely a `{include content}` helyére kerül a layoutban, és újra definiálja a `title` blokkot, amely felülírja a `{block title}`-t a layoutban. Próbálja meg elképzelni az eredményt.


Sablonok keresése
-----------------

Nem kell a presenterekben megadnia, hogy melyik sablont kell renderelni, a keretrendszer maga vezeti le az utat, és megspórolja Önnek az írást.

Ha olyan könyvtárstruktúrát használ, ahol minden presenternek saját könyvtára van, egyszerűen helyezze el a sablont ebben a könyvtárban az akció (ill. view) nevével, azaz a `default` akcióhoz használja a `default.latte` sablont:

/--pre
app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── <b>default.latte</b>
\--

Ha olyan struktúrát használ, ahol a presenterek egy könyvtárban vannak, a sablonok pedig a `templates` mappában, mentse el vagy a `<Presenter>.<view>.latte` vagy a `<Presenter>/<view>.latte` fájlba:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>Home.default.latte</b>  ← 1. változat
        └── <b>Home/</b>
            └── <b>default.latte</b>   ← 2. változat
\--

A `templates` könyvtár egy szinttel feljebb is elhelyezkedhet, azaz ugyanazon a szinten, mint a presenter osztályokat tartalmazó könyvtár.

Ha a sablon nem található, a presenter [404 - page not found hibával |presenters#Hiba 404 és társai] válaszol.

A view-t a `$this->setView('jineView')` segítségével változtathatja meg. Közvetlenül is megadhatja a sablonfájlt a `$this->template->setFile('/path/to/template.latte')` segítségével.

.[note]
A fájlokat, ahol a sablonokat keresi, meg lehet változtatni a [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()] metódus felülírásával, amely visszaadja a lehetséges fájlnevek tömbjét.


Layout sablon keresése
----------------------

A Nette automatikusan megkeresi a layout fájlt is.

Ha olyan könyvtárstruktúrát használ, ahol minden presenternek saját könyvtára van, helyezze el a layoutot vagy a presenter mappájában, ha csak rá specifikus, vagy egy szinttel feljebb, ha több presenter számára közös:

/--pre
app/
└── Presentation/
    ├── <b>@layout.latte</b>           ← közös layout
    └── Home/
        ├── <b>@layout.latte</b>       ← csak a Home presenterhez
        ├── HomePresenter.php
        └── default.latte
\--

Ha olyan struktúrát használ, ahol a presenterek egy könyvtárban vannak, a sablonok pedig a `templates` mappában, a layoutot ezeken a helyeken várja:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>@layout.latte</b>       ← közös layout
        ├── <b>Home.@layout.latte</b>  ← csak a Home-hoz, 1. változat
        └── <b>Home/</b>
            └── <b>@layout.latte</b>   ← csak a Home-hoz, 2. változat
\--

Ha a presenter egy modulban található, akkor további könyvtárszinteken is keresni fog, a modul beágyazási mélységétől függően.

A layout nevét a `$this->setLayout('layoutAdmin')` segítségével lehet megváltoztatni, és akkor a `@layoutAdmin.latte` fájlban várja. Közvetlenül is megadhatja a layout sablonfájlt a `$this->setLayout('/path/to/template.latte')` segítségével.

A `$this->setLayout(false)` vagy a `{layout none}` tag használatával a sablonon belül kikapcsolható a layout keresése.

.[note]
A fájlokat, ahol a layout sablonokat keresi, meg lehet változtatni a [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] metódus felülírásával, amely visszaadja a lehetséges fájlnevek tömbjét.


Változók a sablonban
--------------------

Változókat úgy adunk át a sablonnak, hogy beírjuk őket a `$this->template`-be, és utána lokális változókként érhetők el a sablonban:

```php
$this->template->article = $this->articles->getById($id);
```

Így egyszerűen bármilyen változót átadhatunk a sablonoknak. Robusztus alkalmazások fejlesztésekor azonban hasznosabb korlátozni magunkat. Például úgy, hogy explicit módon definiáljuk a sablon által várt változók listáját és azok típusait. Ennek köszönhetően a PHP ellenőrizni tudja a típusokat, az IDE helyesen tud súgni, és a statikus analízis felfedezheti a hibákat.

És hogyan definiálunk egy ilyen listát? Egyszerűen egy osztály és annak property-jei formájában. Nevezzük el hasonlóan a presenterhez, csak `Template` végződéssel:

```php
/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// és további változók
}
```

A `$this->template` objektum a presenterben mostantól az `ArticleTemplate` osztály példánya lesz. Így a PHP ellenőrizni fogja a deklarált típusokat íráskor. És a PHP 8.2-es verziójától kezdve figyelmeztet a nem létező változóba való írásra is, korábbi verziókban ugyanezt a [Nette\SmartObject |utils:smartobject] trait használatával lehet elérni.

Az `@property-read` annotáció az IDE-nek és a statikus analízisnek szól, ennek köszönhetően működni fog a súgó, lásd "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

A súgó luxusát a sablonokban is élvezheti, csak telepíteni kell a PhpStorm-ba a Latte plugint, és a sablon elejére beírni az osztály nevét, további információk a "Latte: hogyan a típusrendszerre":https://blog.nette.org/hu/latte-how-to-use-type-system cikkben:

```latte
{templateType App\Presentation\Article\ArticleTemplate}
...
```

Így működnek a sablonok a komponensekben is, csak be kell tartani a névkonvenciót, és például a `FifteenControl` komponenshez létrehozni egy `FifteenTemplate` sablonosztályt.

Ha a `$template`-et egy másik osztály példányaként kell létrehoznia, használja a `createTemplate()` metódust:

```php
public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}
```


Alapértelmezett változók
------------------------

A presenterek és komponensek automatikusan átadnak néhány hasznos változót a sablonoknak:

- `$basePath` az abszolút URL elérési út a gyökérkönyvtárhoz (pl. `/eshop`)
- `$baseUrl` az abszolút URL a gyökérkönyvtárhoz (pl. `http://localhost/eshop`)
- `$user` a [felhasználót reprezentáló |security:authentication] objektum
- `$presenter` az aktuális presenter
- `$control` az aktuális komponens vagy presenter
- `$flashes` a `flashMessage()` függvénnyel küldött [üzenetek |presenters#Flash üzenetek] tömbje

Ha saját sablonosztályt használ, ezek a változók átadódnak, ha létrehoz hozzájuk property-t.


Linkek létrehozása
------------------

A sablonban a linkek más presenterekhez & akciókhoz a következőképpen hozhatók létre:

```latte
<a n:href="Product:show">termék részletei</a>
```

Az `n:href` attribútum nagyon praktikus a HTML `<a>` tag-ekhez. Ha máshol szeretnénk kiírni a linket, például szövegben, használjuk a `{link}`-et:

```latte
A cím: {link Home:default}
```

További információkat az [URL linkek létrehozása|creating-links] fejezetben talál.


Saját szűrők, tag-ek stb.
-------------------------

A Latte sablonrendszert ki lehet bővíteni saját szűrőkkel, függvényekkel, tag-ekkel stb. Ezt meg lehet tenni közvetlenül a `render<View>` vagy `beforeRender()` metódusban:

```php
public function beforeRender(): void
{
	// szűrő hozzáadása
	$this->template->addFilter('foo', /* ... */);

	// vagy közvetlenül konfiguráljuk a Latte\Engine objektumot
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}
```

A Latte 3-as verziója fejlettebb módszert kínál, mégpedig egy [extension |latte:extending-latte#Latte Extension] létrehozását minden webprojekthez. Egy ilyen osztály töredékes példája:

```php
namespace App\Presentation\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}
```

Regisztráljuk a [konfiguráció |configuration#Latte sablonok] segítségével:

```neon
latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension
```


Fordítás
--------

Ha többnyelvű alkalmazást programoz, valószínűleg szüksége lesz néhány szöveg lefordítására a sablonban különböző nyelvekre. A Nette Framework erre a célra definiál egy interfészt a fordításhoz [api:Nette\Localization\Translator], amelynek egyetlen metódusa van, a `translate()`. Ez fogadja az üzenetet `$message`, ami általában egy string, és tetszőleges további paramétereket. Feladata a lefordított string visszaadása. A Nette-ben nincs alapértelmezett implementáció, választhat igényei szerint több kész megoldás közül, amelyeket a [Componette |https://componette.org/search/localization] oldalon talál. Dokumentációjukban megtudhatja, hogyan konfigurálja a translatort.

A sablonoknak beállítható egy fordító, amelyet [átkérünk |dependency-injection:passing-dependencies], a `setTranslator()` metódussal:

```php
protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}
```

A translatort alternatívaként be lehet állítani a [konfiguráció |configuration#Latte sablonok] segítségével is:

```neon
latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```

Ezután a fordítót például használhatjuk `|translate` szűrőként, beleértve a kiegészítő paramétereket is, amelyek átadódnak a `translate()` metódusnak (lásd `foo, bar`):

```latte
<a href="basket">{='Kosár'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
```

Vagy aláhúzásos tagként:

```latte
<a href="basket">{_'Kosár'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
```

A sablon egy szakaszának fordításához létezik egy páros `{translate}` tag (Latte 2.11-től, korábban a `{_}` tag volt használatos):

```latte
<a href="order">{translate}Rendelés{/translate}</a>
<a href="order">{translate foo, bar}Rendelés{/translate}</a>
```

A translator alapértelmezés szerint futásidőben hívódik meg a sablon renderelésekor. A Latte 3-as verziója azonban képes az összes statikus szöveget már a sablon fordítása során lefordítani. Ezzel teljesítményt takarítunk meg, mert minden string csak egyszer fordítódik le, és az eredményül kapott fordítás beíródik a lefordított formába. A cache könyvtárban így több lefordított sablonverzió jön létre, minden nyelvre egy. Ehhez elegendő csak a nyelvet második paraméterként megadni:

```php
protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}
```

Statikus szöveg alatt például a `{_'hello'}` vagy `{translate}hello{/translate}` értendő. A nem statikus szövegek, mint például a `{_$foo}`, továbbra is futásidőben fordítódnak.

Sablonok

A Nette a Latte sablonrendszert használja. Egyrészt azért, mert ez a legbiztonságosabb sablonrendszer PHP-hoz, másrészt pedig a legintuitívabb rendszer. Nem kell sok újat tanulnia, elegendő a PHP ismerete és néhány tag.

Gyakori, hogy egy oldal egy layout sablonból + az adott akció sablonjából áll össze. Így nézhet ki például egy layout sablon, figyelje meg a {block} blokkokat és a {include} taget:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}Saját Alkalmazás{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

És ez lesz az akció sablonja:

{block title}Kezdőlap{/block}

{block content}
<h1>Kezdőlap</h1>
...
{/block}

Ez definiálja a content blokkot, amely a {include content} helyére kerül a layoutban, és újra definiálja a title blokkot, amely felülírja a {block title}-t a layoutban. Próbálja meg elképzelni az eredményt.

Sablonok keresése

Nem kell a presenterekben megadnia, hogy melyik sablont kell renderelni, a keretrendszer maga vezeti le az utat, és megspórolja Önnek az írást.

Ha olyan könyvtárstruktúrát használ, ahol minden presenternek saját könyvtára van, egyszerűen helyezze el a sablont ebben a könyvtárban az akció (ill. view) nevével, azaz a default akcióhoz használja a default.latte sablont:

app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Ha olyan struktúrát használ, ahol a presenterek egy könyvtárban vannak, a sablonok pedig a templates mappában, mentse el vagy a <Presenter>.<view>.latte vagy a <Presenter>/<view>.latte fájlba:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1. változat
        └── Home/
            └── default.latte   ← 2. változat

A templates könyvtár egy szinttel feljebb is elhelyezkedhet, azaz ugyanazon a szinten, mint a presenter osztályokat tartalmazó könyvtár.

Ha a sablon nem található, a presenter 404 – page not found hibával válaszol.

A view-t a $this->setView('jineView') segítségével változtathatja meg. Közvetlenül is megadhatja a sablonfájlt a $this->template->setFile('/path/to/template.latte') segítségével.

A fájlokat, ahol a sablonokat keresi, meg lehet változtatni a formatTemplateFiles() metódus felülírásával, amely visszaadja a lehetséges fájlnevek tömbjét.

Layout sablon keresése

A Nette automatikusan megkeresi a layout fájlt is.

Ha olyan könyvtárstruktúrát használ, ahol minden presenternek saját könyvtára van, helyezze el a layoutot vagy a presenter mappájában, ha csak rá specifikus, vagy egy szinttel feljebb, ha több presenter számára közös:

app/
└── Presentation/
    ├── @layout.latte           ← közös layout
    └── Home/
        ├── @layout.latte       ← csak a Home presenterhez
        ├── HomePresenter.php
        └── default.latte

Ha olyan struktúrát használ, ahol a presenterek egy könyvtárban vannak, a sablonok pedig a templates mappában, a layoutot ezeken a helyeken várja:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← közös layout
        ├── Home.@layout.latte  ← csak a Home-hoz, 1. változat
        └── Home/
            └── @layout.latte   ← csak a Home-hoz, 2. változat

Ha a presenter egy modulban található, akkor további könyvtárszinteken is keresni fog, a modul beágyazási mélységétől függően.

A layout nevét a $this->setLayout('layoutAdmin') segítségével lehet megváltoztatni, és akkor a @layoutAdmin.latte fájlban várja. Közvetlenül is megadhatja a layout sablonfájlt a $this->setLayout('/path/to/template.latte') segítségével.

A $this->setLayout(false) vagy a {layout none} tag használatával a sablonon belül kikapcsolható a layout keresése.

A fájlokat, ahol a layout sablonokat keresi, meg lehet változtatni a formatLayoutTemplateFiles() metódus felülírásával, amely visszaadja a lehetséges fájlnevek tömbjét.

Változók a sablonban

Változókat úgy adunk át a sablonnak, hogy beírjuk őket a $this->template-be, és utána lokális változókként érhetők el a sablonban:

$this->template->article = $this->articles->getById($id);

Így egyszerűen bármilyen változót átadhatunk a sablonoknak. Robusztus alkalmazások fejlesztésekor azonban hasznosabb korlátozni magunkat. Például úgy, hogy explicit módon definiáljuk a sablon által várt változók listáját és azok típusait. Ennek köszönhetően a PHP ellenőrizni tudja a típusokat, az IDE helyesen tud súgni, és a statikus analízis felfedezheti a hibákat.

És hogyan definiálunk egy ilyen listát? Egyszerűen egy osztály és annak property-jei formájában. Nevezzük el hasonlóan a presenterhez, csak Template végződéssel:

/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// és további változók
}

A $this->template objektum a presenterben mostantól az ArticleTemplate osztály példánya lesz. Így a PHP ellenőrizni fogja a deklarált típusokat íráskor. És a PHP 8.2-es verziójától kezdve figyelmeztet a nem létező változóba való írásra is, korábbi verziókban ugyanezt a Nette\SmartObject trait használatával lehet elérni.

Az @property-read annotáció az IDE-nek és a statikus analízisnek szól, ennek köszönhetően működni fog a súgó, lásd PhpStorm and code completion for $this⁠-⁠>⁠template.

A súgó luxusát a sablonokban is élvezheti, csak telepíteni kell a PhpStorm-ba a Latte plugint, és a sablon elejére beírni az osztály nevét, további információk a Latte: hogyan a típusrendszerre cikkben:

{templateType App\Presentation\Article\ArticleTemplate}
...

Így működnek a sablonok a komponensekben is, csak be kell tartani a névkonvenciót, és például a FifteenControl komponenshez létrehozni egy FifteenTemplate sablonosztályt.

Ha a $template-et egy másik osztály példányaként kell létrehoznia, használja a createTemplate() metódust:

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Alapértelmezett változók

A presenterek és komponensek automatikusan átadnak néhány hasznos változót a sablonoknak:

  • $basePath az abszolút URL elérési út a gyökérkönyvtárhoz (pl. /eshop)
  • $baseUrl az abszolút URL a gyökérkönyvtárhoz (pl. http://localhost/eshop)
  • $user a felhasználót reprezentáló objektum
  • $presenter az aktuális presenter
  • $control az aktuális komponens vagy presenter
  • $flashes a flashMessage() függvénnyel küldött üzenetek tömbje

Ha saját sablonosztályt használ, ezek a változók átadódnak, ha létrehoz hozzájuk property-t.

Linkek létrehozása

A sablonban a linkek más presenterekhez & akciókhoz a következőképpen hozhatók létre:

<a n:href="Product:show">termék részletei</a>

Az n:href attribútum nagyon praktikus a HTML <a> tag-ekhez. Ha máshol szeretnénk kiírni a linket, például szövegben, használjuk a {link}-et:

A cím: {link Home:default}

További információkat az URL linkek létrehozása fejezetben talál.

Saját szűrők, tag-ek stb.

A Latte sablonrendszert ki lehet bővíteni saját szűrőkkel, függvényekkel, tag-ekkel stb. Ezt meg lehet tenni közvetlenül a render<View> vagy beforeRender() metódusban:

public function beforeRender(): void
{
	// szűrő hozzáadása
	$this->template->addFilter('foo', /* ... */);

	// vagy közvetlenül konfiguráljuk a Latte\Engine objektumot
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

A Latte 3-as verziója fejlettebb módszert kínál, mégpedig egy extension létrehozását minden webprojekthez. Egy ilyen osztály töredékes példája:

namespace App\Presentation\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}

Regisztráljuk a konfiguráció segítségével:

latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension

Fordítás

Ha többnyelvű alkalmazást programoz, valószínűleg szüksége lesz néhány szöveg lefordítására a sablonban különböző nyelvekre. A Nette Framework erre a célra definiál egy interfészt a fordításhoz Nette\Localization\Translator, amelynek egyetlen metódusa van, a translate(). Ez fogadja az üzenetet $message, ami általában egy string, és tetszőleges további paramétereket. Feladata a lefordított string visszaadása. A Nette-ben nincs alapértelmezett implementáció, választhat igényei szerint több kész megoldás közül, amelyeket a Componette oldalon talál. Dokumentációjukban megtudhatja, hogyan konfigurálja a translatort.

A sablonoknak beállítható egy fordító, amelyet átkérünk, a setTranslator() metódussal:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

A translatort alternatívaként be lehet állítani a konfiguráció segítségével is:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Ezután a fordítót például használhatjuk |translate szűrőként, beleértve a kiegészítő paramétereket is, amelyek átadódnak a translate() metódusnak (lásd foo, bar):

<a href="basket">{='Kosár'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

Vagy aláhúzásos tagként:

<a href="basket">{_'Kosár'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

A sablon egy szakaszának fordításához létezik egy páros {translate} tag (Latte 2.11-től, korábban a {_} tag volt használatos):

<a href="order">{translate}Rendelés{/translate}</a>
<a href="order">{translate foo, bar}Rendelés{/translate}</a>

A translator alapértelmezés szerint futásidőben hívódik meg a sablon renderelésekor. A Latte 3-as verziója azonban képes az összes statikus szöveget már a sablon fordítása során lefordítani. Ezzel teljesítményt takarítunk meg, mert minden string csak egyszer fordítódik le, és az eredményül kapott fordítás beíródik a lefordított formába. A cache könyvtárban így több lefordított sablonverzió jön létre, minden nyelvre egy. Ehhez elegendő csak a nyelvet második paraméterként megadni:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Statikus szöveg alatt például a {_'hello'} vagy {translate}hello{/translate} értendő. A nem statikus szövegek, mint például a {_$foo}, továbbra is futásidőben fordítódnak.