Szablony
Nette używa systemu szablonów Latte. Po pierwsze dlatego, że jest to najlepiej zabezpieczony system szablonów dla PHP, a jednocześnie system najbardziej intuicyjny. Nie musisz uczyć się wielu nowych rzeczy, wystarczy znajomość PHP i kilku znaczników.
Jest typowe, że strona składa się z szablonu layoutu + szablonu danej akcji. Tak na przykład może wyglądać szablon
layoutu, zwróć uwagę na bloki {block}
i znacznik {include}
:
<!DOCTYPE html>
<html>
<head>
<title>{block title}Moja Aplikacja{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
A to będzie szablon akcji:
{block title}Strona główna{/block}
{block content}
<h1>Strona główna</h1>
...
{/block}
Definiuje on blok content
, który zostanie wstawiony w miejsce {include content}
w layoucie, a także
redefiniuje blok title
, którym nadpisze {block title}
w layoucie. Spróbuj sobie wyobrazić wynik.
Wyszukiwanie szablonów
Nie musisz w presenterach podawać, jaki szablon ma być wyrenderowany, framework sam wywnioskuje ścieżkę i oszczędzi Ci pisania.
Jeśli używasz struktury katalogów, gdzie każdy presenter ma własny katalog, po prostu umieść szablon w tym katalogu pod
nazwą akcji (resp. view), tj. dla akcji default
użyj szablonu default.latte
:
app/ └── Presentation/ └── Home/ ├── HomePresenter.php └── default.latte
Jeśli używasz struktury, gdzie presentery są razem w jednym katalogu, a szablony w folderze templates
, zapisz
go albo w pliku <Presenter>.<view>.latte
albo <Presenter>/<view>.latte
:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── Home.default.latte ← 1. wariant └── Home/ └── default.latte ← 2. wariant
Katalog templates
może być umieszczony również o poziom wyżej, tj. na tym samym poziomie, co katalog
z klasami presenterów.
Jeśli szablon nie zostanie znaleziony, presenter odpowie błędem 404 – page not found.
View zmienisz za pomocą $this->setView('innyView')
. Można również bezpośrednio określić plik
z szablonem za pomocą $this->template->setFile('/path/to/template.latte')
.
Pliki, w których wyszukiwane są szablony, można zmienić przez nadpisanie metody formatTemplateFiles(), która zwraca tablicę możliwych nazw plików.
Wyszukiwanie szablonu layoutu
Nette również automatycznie wyszukuje plik z layoutem.
Jeśli używasz struktury katalogów, gdzie każdy presenter ma własny katalog, umieść layout albo w folderze z presenterem, jeśli jest specyficzny tylko dla niego, albo o poziom wyżej, jeśli jest wspólny dla wielu presenterów:
app/ └── Presentation/ ├── @layout.latte ← wspólny layout └── Home/ ├── @layout.latte ← tylko dla presentera Home ├── HomePresenter.php └── default.latte
Jeśli używasz struktury, gdzie presentery są razem w jednym katalogu, a szablony w folderze templates
, layout
będzie oczekiwany w tych miejscach:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── @layout.latte ← wspólny layout ├── Home.@layout.latte ← tylko dla Home, 1. wariant └── Home/ └── @layout.latte ← tylko dla Home, 2. wariant
Jeśli presenter znajduje się w module, będzie wyszukiwany również o kolejne poziomy katalogów wyżej, zgodnie z zagnieżdżeniem modułu.
Nazwę layoutu można zmienić za pomocą $this->setLayout('layoutAdmin')
, a wtedy będzie oczekiwany w pliku
@layoutAdmin.latte
. Można również bezpośrednio określić plik z szablonem layoutu za pomocą
$this->setLayout('/path/to/template.latte')
.
Za pomocą $this->setLayout(false)
lub znacznika {layout none}
wewnątrz szablonu wyszukiwanie
layoutu zostanie wyłączone.
Pliki, w których wyszukiwane są szablony layoutu, można zmienić przez nadpisanie metody formatLayoutTemplateFiles(), która zwraca tablicę możliwych nazw plików.
Zmienne w szablonie
Zmienne do szablonu przekazujemy tak, że zapisujemy je do $this->template
, a potem mamy je dostępne w
szablonie jako zmienne lokalne:
$this->template->article = $this->articles->getById($id);
W ten prosty sposób możemy przekazać do szablonów dowolne zmienne. Jednak przy tworzeniu solidnych aplikacji bywa bardziej użyteczne ograniczenie się. Na przykład tak, że jawnie zdefiniujemy wykaz zmiennych, których oczekuje szablon, oraz ich typów. Dzięki temu PHP będzie mógł kontrolować typy, IDE poprawnie podpowiadać, a analiza statyczna wykrywać błędy.
A jak taki wykaz zdefiniujemy? Po prostu w postaci klasy i jej właściwości. Nazwiemy ją podobnie jak presenter, tylko z
Template
na końcu:
/**
* @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;
// i inne zmienne
}
Obiekt $this->template
w presenterze będzie teraz instancją klasy ArticleTemplate
. Więc PHP
podczas zapisu będzie kontrolował zadeklarowane typy. A począwszy od wersji PHP 8.2 powiadomi również o zapisie do
nieistniejącej zmiennej, w poprzednich wersjach tego samego można osiągnąć używając traity Nette\SmartObject.
Adnotacja @property-read
jest przeznaczona dla IDE i analizy statycznej, dzięki niej będzie działać
podpowiadanie, zobacz PhpStorm and code
completion for $this->template.

Luksusu podpowiadania możesz sobie pozwolić również w szablonach, wystarczy zainstalować w PhpStorm wtyczkę dla Latte i podać na początku szablonu nazwę klasy, więcej w artykule Latte: jak na typowy system:
{templateType App\Presentation\Article\ArticleTemplate}
...
Tak działają również szablony w komponentach, wystarczy tylko przestrzegać konwencji nazewnictwa i dla komponentu np.
FifteenControl
utworzyć klasę szablonu FifteenTemplate
.
Jeśli potrzebujesz utworzyć $template
jako instancję innej klasy, wykorzystaj metodę
createTemplate()
:
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Zmienne domyślne
Presentery i komponenty przekazują do szablonów kilka użytecznych zmiennych automatycznie:
$basePath
to absolutna ścieżka URL do katalogu głównego (np./eshop
)$baseUrl
to absolutny URL do katalogu głównego (np.http://localhost/eshop
)$user
to obiekt reprezentujący użytkownika$presenter
to aktualny presenter$control
to aktualny komponent lub presenter$flashes
tablica wiadomości wysłanych funkcjąflashMessage()
Jeśli używasz własnej klasy szablonu, te zmienne zostaną przekazane, jeśli utworzysz dla nich właściwość.
Tworzenie linków
W szablonie tworzy się linki do innych presenterów & akcji w ten sposób:
<a n:href="Product:show">szczegóły produktu</a>
Atrybut n:href
jest bardzo przydatny dla znaczników HTML <a>
. Jeśli chcemy link wypisać
gdzie indziej, na przykład w tekście, użyjemy {link}
:
Adres to: {link Home:default}
Więcej informacji znajdziesz w rozdziale Tworzenie linków URL.
Własne filtry, znaczniki itp.
System szablonów Latte można rozszerzyć o własne filtry, funkcje, znaczniki itp. Można to zrobić bezpośrednio w
metodzie render<View>
lub beforeRender()
:
public function beforeRender(): void
{
// dodanie filtra
$this->template->addFilter('foo', /* ... */);
// lub konfigurujemy bezpośrednio obiekt Latte\Engine
$latte = $this->template->getLatte();
$latte->addFilterLoader(/* ... */);
}
Latte w wersji 3 oferuje bardziej zaawansowany sposób, a mianowicie utworzenie sobie extension dla każdego projektu internetowego. Przykładowy fragment takiej klasy:
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()),
// ...
];
}
// ...
}
Zarejestrujemy ją za pomocą konfiguracji:
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Tłumaczenie
Jeśli programujesz aplikację wielojęzyczną, prawdopodobnie będziesz potrzebować niektóre teksty w szablonie wypisać w
różnych językach. Nette Framework w tym celu definiuje interfejs do tłumaczenia Nette\Localization\Translator, który ma jedyną
metodę translate()
. Przyjmuje ona wiadomość $message
, co zazwyczaj jest ciągiem znaków, oraz
dowolne inne parametry. Zadaniem jest zwrócenie przetłumaczonego ciągu. W Nette nie ma żadnej domyślnej implementacji,
możesz wybrać według swoich potrzeb spośród kilku gotowych rozwiązań, które znajdziesz na Componette. W ich dokumentacji dowiesz się, jak konfigurować
translator.
Szablonom można ustawić translator, który sobie przekażemy, metodą
setTranslator()
:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Translator alternatywnie można ustawić za pomocą konfiguracji:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Następnie można używać translatora na przykład jako filtra |translate
, w tym z dodatkowymi parametrami,
które zostaną przekazane metodzie translate()
(zobacz foo, bar
):
<a href="basket">{='Koszyk'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Lub jako znacznika z podkreśleniem:
<a href="basket">{_'Koszyk'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Do tłumaczenia fragmentu szablonu istnieje parzysty znacznik {translate}
(od Latte 2.11, wcześniej używano
znacznika {_}
):
<a href="order">{translate}Zamówienie{/translate}</a>
<a href="order">{translate foo, bar}Zamówienie{/translate}</a>
Translator standardowo jest wywoływany w czasie rzeczywistym podczas renderowania szablonu. Latte w wersji 3 jednak potrafi wszystkie statyczne teksty tłumaczyć już podczas kompilacji szablonu. Tym samym oszczędza się wydajność, ponieważ każdy ciąg jest tłumaczony tylko raz, a wynikowe tłumaczenie jest zapisywane w skompilowanej postaci. W katalogu z cache powstaje więc więcej skompilowanych wersji szablonu, jedna dla każdego języka. Do tego wystarczy tylko podać język jako drugi parametr:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Tekstem statycznym jest na przykład {_'hello'}
lub {translate}hello{/translate}
. Teksty
niestatyczne, jak na przykład {_$foo}
, nadal będą tłumaczone w czasie rzeczywistym.