Шаблони

Nette використовує систему шаблонів Latte. Latte використовується тому, що це найбезпечніша система шаблонів для PHP, і водночас найінтуїтивніша та найзрозуміліша. Вам не потрібно вивчати багато нового, достатньо знати PHP і кілька тегів Latte.

Зазвичай сторінка заповнюється з шаблону макета + шаблону дії. Ось як може виглядати шаблон макета, зверніть увагу на блоки {block} і тег {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}Мое приложение{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

А це може бути шаблоном дій:

{block title}Главная страница{/block}

{block content}
<h1>Главная страница</h1>
...
{/block}

Він визначає блок content, який вставляється замість {include content} у макеті, а також перевизначає блок title, який перезаписує {block title} у макеті. Спробуйте уявити собі результат.

Пошук шаблонів

Шлях до шаблонів визначається ведучим за допомогою простої логіки. Він спробує перевірити, чи є один із цих файлів, розташований відносно каталогу класу ведучого, де <Presenter> це ім'я поточного ведучого, а <view> це ім'я поточної події:

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

Якщо шаблон не буде знайдено, він спробує виконати пошук у каталозі templates на один рівень вище, тобто на тому ж рівні, що і каталог з класом presenter.

Якщо шаблон не буде знайдено і там, у відповідь буде видано помилку 404.

Ви також можете змінити вигляд за допомогою $this->setView('jineView'). Або, замість прямого пошуку, вкажіть ім'я файлу шаблону за допомогою $this->template->setFile('/path/to/template.latte').

Файли, в яких здійснюється пошук шаблонів, можна змінити, наклавши метод formatTemplateFiles(), який повертає масив можливих імен файлів.

У цих файлах очікується компонування:

  • templates/<Presenter>/@<layout>.latte
  • templates/<Presenter>.@<layout>.latte
  • templates/@<layout>.latte макет, спільний для кількох доповідачів

Де <Presenter> це ім'я поточного ведучого і <layout> це ім'я макета, яке за замовчуванням дорівнює 'layout'. Ім'я може бути змінено за допомогою $this->setLayout('jinyLayout'), тому будуть випробувані файли @jinyLayout.latte.

Ви також можете безпосередньо вказати ім'я файлу шаблону макета за допомогою $this->setLayout('/path/to/template.latte'). Використання $this->setLayout(false) відключає відстеження макета.

Файли, в яких здійснюється пошук шаблонів макета, можна змінити, наклавши метод formatLayoutTemplateFiles(), який повертає масив можливих імен файлів.

Змінні в шаблоні

Змінні передаються в шаблон шляхом запису їх у $this->template, а потім вони доступні в шаблоні як локальні змінні:

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

Таким чином, ми можемо легко передавати будь-які змінні в шаблони. Однак при розробці надійних додатків часто корисніше обмежити себе. Наприклад, шляхом явного визначення списку змінних, які очікує шаблон, і їхніх типів. Це дозволить PHP перевіряти типи, IDE – правильно шепотіти, а статичний аналіз – виявляти помилки.

І як визначити таке перерахування? Просто у вигляді класу і його властивостей. Ми назвемо його як presenter, але з Template в кінці:

/**
 * @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;

	// та інші змінні
}

Об'єкт $this->template в presenter тепер буде екземпляром класу ArticleTemplate. Таким чином, PHP перевірятиме оголошені типи під час запису. А починаючи з PHP 8.2, він також буде попереджати при записі в неіснуючу змінну; в попередніх версіях цього можна домогтися за допомогою властивості Nette\SmartObject.

Анотація @property-read призначена для IDE і статичного аналізу, вона змусить працювати шепіт, див. PhpStorm і завершення коду для $this->template.

Ви також можете дозволити собі розкіш шепотіти в шаблонах, просто встановіть плагін Latte в PhpStorm і помістіть ім'я класу на початок шаблону, докладнішу інформацію дивіться в статті Latte: як набирати систему:

{templateType App\Presenters\ArticleTemplate}
...

Таким же чином шаблони працюють у компонентах, просто дотримуйтесь угоди про іменування і створіть клас шаблону FifteenTemplate для компонента, наприклад, FifteenControl.

Якщо вам потрібно створити $template як екземпляр іншого класу, використовуйте метод createTemplate():

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

Змінні за замовчуванням

Презентатори та компоненти автоматично передають шаблонам кілька корисних змінних:

  • $basePath – абсолютний URL-шлях до кореневого каталогу (наприклад, /eshop).
  • $baseUrl – це абсолютна URL-адреса кореневого каталогу (наприклад. http://localhost/eshop)
  • $user – це об'єкт, що представляє користувача.
  • $presenter – нинішній ведучий
  • $control – поточний компонент або ведучий
  • $flashes – це масив повідомлень, надісланих функціями flashMessage()

Якщо ви використовуєте користувацький клас шаблону, ці змінні будуть передані, якщо ви створите для них властивість.

Шаблон створює таким чином посилання на інших ведучих і заходи:

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

Атрибут n:href дуже зручний для HTML-тегів. <a>. Якщо ми хочемо вказати посилання в іншому місці, наприклад, у тексті, ми використовуємо {link}:

Adresa je: {link Home:default}

Додаткові відомості див. у розділі Створення посилань URL.

Користувацькі фільтри, теги тощо.

Система шаблонів Latte може бути розширена за допомогою користувацьких фільтрів, функцій, тегів тощо. Це можна зробити безпосередньо в методі render<View> або beforeRender():

public function beforeRender(): void
{
	// додати фільтр
	$this->template->addFilter('foo', /* ... */);

	// або налаштувати об'єкт Latte\Engine безпосередньо
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte версії 3 пропонує більш просунутий спосіб створення розширення для кожного веб-проекту. Ось короткий приклад такого класу:

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

	// ...
}

Ми реєструємо його за допомогою конфігурації:

latte:
	extensions:
		- App\Templating\LatteExtension

Перекладати

Якщо ви програмуєте багатомовний додаток, вам, ймовірно, знадобиться виводити частину тексту в шаблоні різними мовами. Для цього в Nette Framework визначено інтерфейс перекладу Nette\Localization\Translator, який має єдиний метод translate(). Він приймає повідомлення $message, яке зазвичай є рядком, і будь-які інші параметри. Завдання полягає у поверненні перекладеного рядка. У Nette немає реалізації за замовчуванням, ви можете вибрати відповідно до своїх потреб з декількох готових рішень, які можна знайти на Componette. Їх документація підкаже вам, як налаштувати перекладач.

Шаблони можна налаштувати за допомогою перекладача, який нам передадуть, за допомогою методу setTranslator():

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

Крім того, перекладач можна встановити за допомогою конфігурації:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension

Тоді перекладач можна використовувати, наприклад, як фільтр |translate, з додатковими параметрами, переданими методу translate() (див. foo, bar):

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

Або як тег підкреслення:

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

Для перекладу шаблонних розділів існує парний тег {translate} (починаючи з версії Latte 2.11, раніше використовувався тег {_} ):

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

Перекладач викликається за замовчуванням під час виконання шаблону під час рендерингу. Однак у версії 3 Latte може перекладати весь статичний текст під час компіляції шаблону. Це економить продуктивність, оскільки кожен рядок перекладається лише один раз, а результат перекладу записується до скомпільованої форми. Це створює кілька скомпільованих версій шаблону в кеш-пам'яті, по одній для кожної мови. Для цього вам потрібно лише вказати мову як другий параметр:

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

Під статичним текстом мається на увазі, наприклад, {_'hello'} або {translate}hello{/translate}. Нестатичний текст, такий як {_$foo}, буде продовжувати компілюватися на льоту.

версію: 4.0