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.
Sablon keresés
A prezenterekben nem kell megadnia, hogy melyik sablont kell megjeleníteni; a keretrendszer automatikusan meghatározza az útvonalat, megkönnyítve ezzel a kódolást.
Ha olyan könyvtárstruktúrát használ, ahol minden prezenternek saját könyvtára van, egyszerűen helyezze el a sablont
ebben a könyvtárban az akció (pl. nézet) neve alatt. Például a default
művelethez használja a
default.latte
sablont:
app/ └── UI/ └── Home/ ├── HomePresenter.php └── default.latte
Ha olyan struktúrát használ, ahol az előadók együttesen egy könyvtárban vannak, a sablonok pedig a
templates
mappában, mentse el vagy egy fájlban <Presenter>.<view>.latte
vagy a
<Presenter>/<view>.latte
:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── Home.default.latte ← 1st variant └── Home/ └── default.latte ← 2nd variant
A templates
könyvtár egy szinttel feljebb is elhelyezhető, ugyanazon a szinten, mint az előadói osztályokat
tartalmazó könyvtár.
Ha a sablon nem található, a prezenter 404 – page not found hibával válaszol.
A nézetet a $this->setView('anotherView')
segítségével lehet megváltoztatni. Lehetőség van a
sablonfájl közvetlen megadására is a $this->template->setFile('/path/to/template.latte')
segítségével.
A fájlokat, amelyekben a sablonok keresése történik, a formatTemplateFiles() metódus felülbírálásával lehet megváltoztatni, amely a lehetséges fájlnevek tömbjét adja vissza.
Layout sablon keresés
A Nette automatikusan megkeresi az elrendezési fájlt is.
Ha olyan könyvtárstruktúrát használ, ahol minden előadónak saját könyvtára van, akkor az elrendezést vagy az előadóval közös mappába helyezze el, ha csak rá jellemző, vagy egy szinttel feljebb, ha több előadó számára közös:
app/ └── UI/ ├── @layout.latte ← common layout └── Home/ ├── @layout.latte ← only for Home presenter ├── HomePresenter.php └── default.latte
Ha olyan struktúrát használ, ahol az előadók egy könyvtárban vannak csoportosítva, a sablonok pedig a
templates
mappában találhatók, az elrendezés a következő helyeken várható:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── @layout.latte ← common layout ├── Home.@layout.latte ← only for Home, 1st variant └── Home/ └── @layout.latte ← only for Home, 2nd variant
Ha a bemutató egy modulban van, akkor a modul beágyazottságának megfelelően a könyvtárfában feljebb is keresni fog.
Az elrendezés nevét a $this->setLayout('layoutAdmin')
segítségével lehet megváltoztatni, majd a
@layoutAdmin.latte
fájlban várjuk el. Az elrendezés sablonfájlt közvetlenül is megadhatja a
$this->setLayout('/path/to/template.latte')
segítségével.
A $this->setLayout(false)
vagy a {layout none}
címke használata a sablonon belül kikapcsolja az
elrendezéskeresést.
A fájlok, amelyekben az elrendezési sablonok keresése történik, megváltoztathatók a formatLayoutTemplateFiles() metódus felülbírálásával, amely a lehetséges fájlnevek 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\UI\Article\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ájaflashMessage()
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
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\UI\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()),
// ...
];
}
// ...
}
A configuration segítségével regisztráljuk:
latte:
extensions:
- App\UI\Accessory\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(@Nette\Localization\Translator)
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.