Nette Documentation Preview

syntax
Sablonok
********

.[perex]
A Nette a [Latte |latte:] sablonrendszert használja. A Latte-t azért használjuk, mert ez a legbiztonságosabb sablonrendszer a PHP számára, ugyanakkor a legintuitívabb rendszer. Nem kell sok újat tanulnod, csak ismerned kell a PHP-t és néhány Latte taget.

Az a szokásos, hogy az oldal az elrendezési sablonból + az akció sablonból készül el. Így nézhet ki egy layout sablon, figyeld meg a `{block}` blokkokat és a `{include}` címkét :

```latte
<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>
```

És ez lehet az akció sablon:

```latte
{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}
```

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


Sablonok keresése .[#toc-search-for-templates]
----------------------------------------------

A sablonok elérési útvonalát egyszerű logika szerint vezetjük le. Megpróbálja megnézni, hogy létezik-e valamelyik sablonfájl ahhoz a könyvtárhoz képest, ahol a prezenter osztály található, ahol a `<Presenter>` az aktuális prezenter neve és `<view>` az aktuális művelet neve:

- `templates/<Presenter>/<view>.latte`
- `templates/<Presenter>.<view>.latte`

Ha a sablon nem található, a program megpróbál a `templates` könyvtárban keresni egy szinttel feljebb, azaz ugyanazon a szinten, mint a bemutató osztályt tartalmazó könyvtár.

Ha a sablon ott sem található, a válasz [404-es hiba |presenters#Error 404 etc.].

A nézetet a `$this->setView('otherView')` segítségével is megváltoztathatja. Vagy a keresés helyett közvetlenül megadhatja a sablonfájl nevét a `$this->template->setFile('/path/to/template.latte')` segítségével.

.[note]
A [formatTemplateFiles |api:Nette\Application\UI\Presenter::formatTemplateFiles()] metódus felülbírálásával módosíthatja azokat az elérési utakat, ahol a sablonok keresése történik, amely a lehetséges fájl elérési utak tömbjét adja vissza.

Az elrendezés a következő fájlokban várható:

- `templates/<Presenter>/@<layout>.latte`
- `templates/<Presenter>.@<layout>.latte`
- `templates/@<layout>.latte` több előadónál közös elrendezés

`<Presenter>` az aktuális előadó neve és `<layout>` az elrendezés neve, amely alapértelmezés szerint `'layout'`. A név megváltoztatható a `$this->setLayout('otherLayout')` segítségével, így a `@otherLayout.latte` fájlokat próbálja meg.

Az elrendezéssablon fájlnevét közvetlenül is megadhatja a `$this->setLayout('/path/to/template.latte')` segítségével. A `$this->setLayout(false)` használata letiltja az elrendezés keresését.

.[note]
A [formatLayoutTemplateFiles |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] metódus felülbírálásával módosíthatja a sablonok keresési útvonalait, amely a lehetséges fájlútvonalak tömbjét adja vissza.


Változók a sablonban .[#toc-variables-in-the-template]
------------------------------------------------------

A változókat a `$this->template` címre írva adjuk át a sablonba, majd a sablonban helyi változóként állnak rendelkezésre:

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

Így könnyen átadhatunk bármilyen változót a sablonoknak. Robusztus alkalmazások fejlesztésénél azonban gyakran hasznosabb, ha korlátozzuk magunkat. Például úgy, hogy explicit módon definiáljuk a sablon által elvárt változók listáját és azok típusát. Ez lehetővé teszi a PHP számára a típusellenőrzést, az IDE számára a helyes automatikus kitöltést, a statikus elemzés számára pedig a hibák felderítését.

És hogyan definiáljunk egy ilyen felsorolást? Egyszerűen egy osztály és annak tulajdonságai formájában. A presenterhez hasonlóan nevezzük el, de a végén `Template` címmel:

```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 egyéb változók
}
```

A `$this->template` objektum a prezenterben mostantól a `ArticleTemplate` osztály példánya lesz. A PHP tehát ellenőrizni fogja a deklarált típusokat íráskor. A PHP 8.2-től kezdve pedig figyelmeztetni fog a nem létező változóba való írásra is, a korábbi verziókban ugyanezt a [Nette\SmartObject |utils:smartobject] tulajdonsággal lehet elérni.

A `@property-read` annotáció az IDE és a statikus elemzés számára készült, ez teszi működőképessé az automatikus kitöltést, lásd "PhpStorm és kódkiegészítés $this->template számára":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

A sablonokban is megengedheted magadnak a suttogás luxusát, csak telepítsd a Latte plugint a PhpStormban, és add meg az osztály nevét a sablon elején, lásd a "Latte: hogyan kell tipizálni a rendszert":https://blog.nette.org/hu/latte-hogyan-kell-hasznalni-a-tipusrendszert című cikket:

```latte
{templateType App\Presenters\ArticleTemplate}
...
```

Így működnek a sablonok a komponensekben is, csak kövesse a névadási konvenciót, és hozzon létre egy sablon osztályt `FifteenTemplate` a komponenshez pl. `FifteenControl`.

Ha a `$template` egy másik osztály példányaként kell létrehozni, használjuk 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 .[#toc-default-variables]
--------------------------------------------------

A prezenterek és komponensek számos hasznos változót adnak át automatikusan a sablonoknak:

- `$basePath` egy abszolút URL elérési útvonal a gyökérkönyvtárhoz (például `/CD-collection`).
- `$baseUrl` egy abszolút URL cím a gyökérkönyvtárhoz (pl. `http://localhost/CD-collection`)
- `$user` egy objektum, [amely a felhasználót képviseli |security:authentication]
- `$presenter` az aktuális előadó
- `$control` az aktuális komponens vagy bemutató
- `$flashes` a módszer által küldött [üzenetek |presenters#flash-messages] listája `flashMessage()`

Ha egyéni sablonosztályt használ, ezeket a változókat akkor adja át, ha létrehoz egy tulajdonságot számukra.


Linkek létrehozása .[#toc-creating-links]
-----------------------------------------

A sablonban az alábbiak szerint hozunk létre linkeket más előadókra és műveletekre:

```latte
<a n:href="Product:show">detail</a>
```

A `n:href` attribútum nagyon hasznos a HTML címkékhez. `<a>`. Ha a linket máshol, például a szövegben akarjuk kiírni, akkor a `{link}`-t használjuk:

```latte
URL is: {link Home:default}
```

További információért lásd: [Linkek létrehozása |Creating Links].


Egyéni szűrők, címkék stb. .[#toc-custom-filters-tags-etc]
----------------------------------------------------------

A Latte templating rendszer kibővíthető egyéni szűrőkkel, függvényekkel, címkékkel stb. Ez közvetlenül a `render<View>` vagy a `beforeRender()` metódusban:

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

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

A Latte 3. verziója egy fejlettebb módszert kínál, amely minden egyes webes projekthez egy [bővítményt |latte:creating-extension] hoz létre. Íme egy durva példa egy ilyen osztályra:

```php
namespace App\Templating;

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()),
			// ...
		];
	}

	// ...
}
```

A [configuration |configuration#Latte] segítségével regisztráljuk:

```neon
latte:
	extensions:
		- App\Templating\LatteExtension
```


Fordítás .[#toc-translating]
----------------------------

Ha többnyelvű alkalmazást programoz, valószínűleg a sablonban lévő szöveg egy részét különböző nyelveken kell majd kiadnia. Ehhez a Nette Framework definiál egy fordítási felületet [api:Nette\Localization\Translator], amelynek egyetlen metódusa a `translate()`. Ez elfogadja a `$message` üzenetet, amely általában egy karakterlánc, és bármilyen más paramétert. A feladat a lefordított karakterlánc visszaadása.
A Nette-ben nincs alapértelmezett implementáció, a [Componette-en |https://componette.org/search/localization] található számos kész megoldás közül választhatunk igényeinknek megfelelően. A dokumentációjukból megtudhatjuk, hogyan kell a fordítót konfigurálni.

A sablonokat a `setTranslator()` metódus segítségével állíthatjuk be egy fordítóval, amelyet [átadunk mag |dependency-injection:passing-dependencies]unknak:

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

Alternatívaként a fordítót a [konfiguráció |configuration#Latte] segítségével is beállíthatjuk:

```neon
latte:
	extensions:
		- Latte\Essential\TranslatorExtension
```

A fordító ekkor például a `|translate` szűrőként használható, a `translate()` metódusnak átadott további paraméterekkel (lásd `foo, bar`):

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

Vagy aláhúzáscímkeként:

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

A sablonrészlet fordításához van egy párosított tag `{translate}` (a Latte 2.11 óta, korábban a `{_}` taget használták ):

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

A fordítót alapértelmezés szerint futásidőben hívja meg a sablon renderelésekor. A Latte 3. verziója azonban képes lefordítani az összes statikus szöveget a sablon összeállítása során. Ez teljesítményt takarít meg, mivel minden egyes karakterláncot csak egyszer fordít le, és az így kapott fordítás a lefordított formába kerül. Ez a sablon több lefordított változatát hozza létre a gyorsítótárban, egyet-egyet minden nyelvhez. Ehhez csak a nyelvet kell megadni második paraméterként:

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

Statikus szöveg alatt például a `{_'hello'}` vagy a `{translate}hello{/translate}` szöveget értjük. A nem statikus szövegek, például a `{_$foo}`, továbbra is menet közben kerülnek lefordításra.

Sablonok

A Nette a Latte sablonrendszert használja. A Latte-t azért használjuk, mert ez a legbiztonságosabb sablonrendszer a PHP számára, ugyanakkor a legintuitívabb rendszer. Nem kell sok újat tanulnod, csak ismerned kell a PHP-t és néhány Latte taget.

Az a szokásos, hogy az oldal az elrendezési sablonból + az akció sablonból készül el. Így nézhet ki egy layout sablon, figyeld meg a {block} blokkokat és a {include} címkét :

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

És ez lehet az akció sablon:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

Meghatározza a content blokkot, amely az elrendezésben a {include content} helyére kerül, és újra definiálja a title blokkot is, amely felülírja az elrendezésben a {block title} blokkot. Próbálja meg elképzelni az eredményt.

Sablonok keresése

A sablonok elérési útvonalát egyszerű logika szerint vezetjük le. Megpróbálja megnézni, hogy létezik-e valamelyik sablonfájl ahhoz a könyvtárhoz képest, ahol a prezenter osztály található, ahol a <Presenter> az aktuális prezenter neve és <view> az aktuális művelet neve:

  • templates/<Presenter>/<view>.latte
  • templates/<Presenter>.<view>.latte

Ha a sablon nem található, a program megpróbál a templates könyvtárban keresni egy szinttel feljebb, azaz ugyanazon a szinten, mint a bemutató osztályt tartalmazó könyvtár.

Ha a sablon ott sem található, a válasz 404-es hiba.

A nézetet a $this->setView('otherView') segítségével is megváltoztathatja. Vagy a keresés helyett közvetlenül megadhatja a sablonfájl nevét a $this->template->setFile('/path/to/template.latte') segítségével.

formatTemplateFiles metódus felülbírálásával módosíthatja azokat az elérési utakat, ahol a sablonok keresése történik, amely a lehetséges fájl elérési utak tömbjét adja vissza.

Az elrendezés a következő fájlokban várható:

  • templates/<Presenter>/@<layout>.latte
  • templates/<Presenter>.@<layout>.latte
  • templates/@<layout>.latte több előadónál közös elrendezés

<Presenter> az aktuális előadó neve és <layout> az elrendezés neve, amely alapértelmezés szerint 'layout'. A név megváltoztatható a $this->setLayout('otherLayout') segítségével, így a @otherLayout.latte fájlokat próbálja meg.

Az elrendezéssablon fájlnevét közvetlenül is megadhatja a $this->setLayout('/path/to/template.latte') segítségével. A $this->setLayout(false) használata letiltja az elrendezés keresését.

formatLayoutTemplateFiles metódus felülbírálásával módosíthatja a sablonok keresési útvonalait, amely a lehetséges fájlútvonalak tömbjét adja vissza.

Változók a sablonban

A változókat a $this->template címre írva adjuk át a sablonba, majd a sablonban helyi változóként állnak rendelkezésre:

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

Így könnyen átadhatunk bármilyen változót a sablonoknak. Robusztus alkalmazások fejlesztésénél azonban gyakran hasznosabb, ha korlátozzuk magunkat. Például úgy, hogy explicit módon definiáljuk a sablon által elvárt változók listáját és azok típusát. Ez lehetővé teszi a PHP számára a típusellenőrzést, az IDE számára a helyes automatikus kitöltést, a statikus elemzés számára pedig a hibák felderítését.

És hogyan definiáljunk egy ilyen felsorolást? Egyszerűen egy osztály és annak tulajdonságai formájában. A presenterhez hasonlóan nevezzük el, de a végén Template címmel:

/**
 * @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 egyéb változók
}

A $this->template objektum a prezenterben mostantól a ArticleTemplate osztály példánya lesz. A PHP tehát ellenőrizni fogja a deklarált típusokat íráskor. A PHP 8.2-től kezdve pedig figyelmeztetni fog a nem létező változóba való írásra is, a korábbi verziókban ugyanezt a Nette\SmartObject tulajdonsággal lehet elérni.

A @property-read annotáció az IDE és a statikus elemzés számára készült, ez teszi működőképessé az automatikus kitöltést, lásd PhpStorm és kódkiegészítés $this->template számára.

A sablonokban is megengedheted magadnak a suttogás luxusát, csak telepítsd a Latte plugint a PhpStormban, és add meg az osztály nevét a sablon elején, lásd a Latte: hogyan kell tipizálni a rendszert című cikket:

{templateType App\Presenters\ArticleTemplate}
...

Így működnek a sablonok a komponensekben is, csak kövesse a névadási konvenciót, és hozzon létre egy sablon osztályt FifteenTemplate a komponenshez pl. FifteenControl.

Ha a $template egy másik osztály példányaként kell létrehozni, használjuk 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 prezenterek és komponensek számos hasznos változót adnak át automatikusan a sablonoknak:

  • $basePath egy abszolút URL elérési útvonal a gyökérkönyvtárhoz (például /CD-collection).
  • $baseUrl egy abszolút URL cím a gyökérkönyvtárhoz (pl. http://localhost/CD-collection)
  • $user egy objektum, amely a felhasználót képviseli
  • $presenter az aktuális előadó
  • $control az aktuális komponens vagy bemutató
  • $flashes a módszer által küldött üzenetek listája flashMessage()

Ha egyéni sablonosztályt használ, ezeket a változókat akkor adja át, ha létrehoz egy tulajdonságot számukra.

A sablonban az alábbiak szerint hozunk létre linkeket más előadókra és műveletekre:

<a n:href="Product:show">detail</a>

A n:href attribútum nagyon hasznos a HTML címkékhez. <a>. Ha a linket máshol, például a szövegben akarjuk kiírni, akkor a {link}-t használjuk:

URL is: {link Home:default}

További információért lásd: Linkek létrehozása.

Egyéni szűrők, címkék stb.

A Latte templating rendszer kibővíthető egyéni szűrőkkel, függvényekkel, címkékkel stb. Ez közvetlenül a render<View> vagy a beforeRender() metódusban:

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

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

A Latte 3. verziója egy fejlettebb módszert kínál, amely minden egyes webes projekthez egy bővítményt hoz létre. Íme egy durva példa egy ilyen osztályra:

namespace App\Templating;

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()),
			// ...
		];
	}

	// ...
}

configuration segítségével regisztráljuk:

latte:
	extensions:
		- App\Templating\LatteExtension

Fordítás

Ha többnyelvű alkalmazást programoz, valószínűleg a sablonban lévő szöveg egy részét különböző nyelveken kell majd kiadnia. Ehhez a Nette Framework definiál egy fordítási felületet Nette\Localization\Translator, amelynek egyetlen metódusa a translate(). Ez elfogadja a $message üzenetet, amely általában egy karakterlánc, és bármilyen más paramétert. A feladat a lefordított karakterlánc visszaadása. A Nette-ben nincs alapértelmezett implementáció, a Componette-en található számos kész megoldás közül választhatunk igényeinknek megfelelően. A dokumentációjukból megtudhatjuk, hogyan kell a fordítót konfigurálni.

A sablonokat a setTranslator() metódus segítségével állíthatjuk be egy fordítóval, amelyet átadunk magunknak:

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

Alternatívaként a fordítót a konfiguráció segítségével is beállíthatjuk:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension

A fordító ekkor például a |translate szűrőként használható, a translate() metódusnak átadott további paraméterekkel (lásd foo, bar):

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

Vagy aláhúzáscímkeként:

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

A sablonrészlet fordításához van egy párosított tag {translate} (a Latte 2.11 óta, korábban a {_} taget használták ):

<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>

A fordítót alapértelmezés szerint futásidőben hívja meg a sablon renderelésekor. A Latte 3. verziója azonban képes lefordítani az összes statikus szöveget a sablon összeállítása során. Ez teljesítményt takarít meg, mivel minden egyes karakterláncot csak egyszer fordít le, és az így kapott fordítás a lefordított formába kerül. Ez a sablon több lefordított változatát hozza létre a gyorsítótárban, egyet-egyet minden nyelvhez. Ehhez csak a nyelvet kell megadni második paraméterként:

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

Statikus szöveg alatt például a {_'hello'} vagy a {translate}hello{/translate} szöveget értjük. A nem statikus szövegek, például a {_$foo}, továbbra is menet közben kerülnek lefordításra.