Nette Documentation Preview

syntax
Сторінка з дописом
******************

.[perex]
Тепер ми створимо ще одну сторінку блогу, яка буде відображати один конкретний допис.


Нам потрібно створити новий render-метод, який отримає одну конкретну статтю і передасть її в шаблон. Мати цей метод у `HomePresenter` не дуже гарно, оскільки ми говоримо про статтю, а не про головну сторінку. Створимо `PostPresenter` у `app/Presentation/Post/`. Цей presenter також потребує підключення до бази даних, тому тут ми знову напишемо конструктор, який вимагатиме підключення до бази даних.

`PostPresenter` може виглядати так:

```php .{file:app/Presentation/Post/PostPresenter.php}
<?php
namespace App\Presentation\Post;

use Nette;
use Nette\Application\UI\Form;

final class PostPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function renderShow(int $id): void
	{
		$this->template->post = $this->database
			->table('posts')
			->get($id);
	}
}
```

Не забудемо вказати правильний простір імен `App\Presentation\Post`, який підпорядковується налаштуванню [мапування presenter'ів |https://github.com/nette-examples/quickstart/blob/v4.0/config/common.neon#L6-L7].

Метод `renderShow` вимагає один аргумент - ID однієї конкретної статті, яка має бути відображена. Потім він завантажує цю статтю з бази даних і передає її в шаблон.

У шаблон `Home/default.latte` вставимо посилання на дію `Post:show`.

```latte .{file:app/Presentation/Home/default.latte}
...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...
```

Тег `{link}` генерує URL-адресу, яка вказує на дію `Post:show`. Він також передає ID допису як аргумент.


Те саме можна записати скорочено за допомогою n:атрибута:

```latte .{file:app/Presentation/Home/default.latte}
...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...
```

Атрибут `n:href` є аналогом тегу `{link}`.



Однак для дії `Post:show` ще не існує шаблону. Можемо спробувати відкрити посилання на цей допис. [Tracy |tracy:] відобразить помилку, оскільки шаблон `Post/show.latte` ще не існує. Якщо ви бачите інше повідомлення про помилку, то, ймовірно, вам доведеться увімкнути `mod_rewrite` на веб-сервері.

Створимо шаблон `Post/show.latte` з таким вмістом:

```latte .{file:app/Presentation/Post/show.latte}
{block content}

<p><a n:href="Home:default">← назад до списку дописів</a></p>

<div class="date">{$post->created_at|date:'F j, Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>
```

Тепер розглянемо окремі частини шаблону.

Перший рядок починає визначення блоку з назвою "content", так само як це було на головній сторінці. Цей блок знову буде відображений у головному шаблоні. Як бачите, відсутній кінцевий тег `{/block}`. Він є необов'язковим.

На наступному рядку є посилання назад на список статей блогу, щоб користувач міг легко переміщатися між списком статей та однією конкретною. Оскільки ми використовуємо атрибут `n:href`, Nette саме подбає про генерацію посилань. Посилання вказує на дію `default` presenter'а `Home` (можна також написати `n:href="Home:"`, оскільки дія з назвою `default` може бути пропущена, вона доповниться автоматично).

Третій рядок форматує виведення дати за допомогою фільтра, який ми вже знаємо.

Четвертий рядок відображає *заголовок* блогу в HTML-тегу `<h1>`. Цей тег містить атрибут, який ви, можливо, не знаєте (`n:block="title"`). Вгадаєте, що він робить? Якщо ви уважно читали попередню частину, то вже знаєте, що це `n:атрибут`. Це ще один їх приклад, який є еквівалентним до:

```latte
{block title}<h1>{$post->title}</h1>{/block}
```

Простіше кажучи, цей блок перевизначає блок з назвою `title`. Цей блок вже визначений у головному *layout* шаблоні (`/app/Presentation/@layout.latte:11`), і так само, як при перевизначенні методів в ООП, точно так само цей блок у головному шаблоні буде перевизначений. Отже, `<title>` сторінки тепер містить заголовок відображеного допису, і для цього нам знадобилося використати лише один простий атрибут `n:block="title"`. Чудово, чи не так?

П'ятий і останній рядок шаблону відображає весь вміст одного конкретного допису.


Перевірка ID допису
===================

Що станеться, якщо хтось змінить ID в URL і вставить якесь неіснуюче `id`? Ми повинні запропонувати користувачеві гарну помилку типу "сторінка не знайдена". Трохи змінимо render-метод у `PostPresenter`:

```php .{file:app/Presentation/Post/PostPresenter.php}
public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Сторінку не знайдено');
	}

	$this->template->post = $post;
}
```

Якщо допис не може бути знайдений, викликом `$this->error(...)` ми відобразимо сторінку помилки 404 зі зрозумілим повідомленням. Зверніть увагу, що в режимі розробки (localhost) ви цю сторінку помилки не побачите. Замість неї з'явиться Tracy з деталями про виняток, що досить зручно для розробки. Якщо ми хочемо побачити обидва режими, достатньо лише змінити аргумент методу `setDebugMode` у файлі `Bootstrap.php`.


Підсумок
========

У нас є база даних з дописами та веб-застосунок, який має два представлення - перше відображає огляд усіх дописів, а друге - один конкретний допис.

{{priority: -1}}
{{sitename: Nette Quickstart}}

Сторінка з дописом

Тепер ми створимо ще одну сторінку блогу, яка буде відображати один конкретний допис.

Нам потрібно створити новий render-метод, який отримає одну конкретну статтю і передасть її в шаблон. Мати цей метод у HomePresenter не дуже гарно, оскільки ми говоримо про статтю, а не про головну сторінку. Створимо PostPresenter у app/Presentation/Post/. Цей presenter також потребує підключення до бази даних, тому тут ми знову напишемо конструктор, який вимагатиме підключення до бази даних.

PostPresenter може виглядати так:

<?php
namespace App\Presentation\Post;

use Nette;
use Nette\Application\UI\Form;

final class PostPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function renderShow(int $id): void
	{
		$this->template->post = $this->database
			->table('posts')
			->get($id);
	}
}

Не забудемо вказати правильний простір імен App\Presentation\Post, який підпорядковується налаштуванню мапування presenter'ів.

Метод renderShow вимагає один аргумент – ID однієї конкретної статті, яка має бути відображена. Потім він завантажує цю статтю з бази даних і передає її в шаблон.

У шаблон Home/default.latte вставимо посилання на дію Post:show.

...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...

Тег {link} генерує URL-адресу, яка вказує на дію Post:show. Він також передає ID допису як аргумент.

Те саме можна записати скорочено за допомогою n:атрибута:

...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...

Атрибут n:href є аналогом тегу {link}.

Однак для дії Post:show ще не існує шаблону. Можемо спробувати відкрити посилання на цей допис. Tracy відобразить помилку, оскільки шаблон Post/show.latte ще не існує. Якщо ви бачите інше повідомлення про помилку, то, ймовірно, вам доведеться увімкнути mod_rewrite на веб-сервері.

Створимо шаблон Post/show.latte з таким вмістом:

{block content}

<p><a n:href="Home:default">← назад до списку дописів</a></p>

<div class="date">{$post->created_at|date:'F j, Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>

Тепер розглянемо окремі частини шаблону.

Перший рядок починає визначення блоку з назвою „content“, так само як це було на головній сторінці. Цей блок знову буде відображений у головному шаблоні. Як бачите, відсутній кінцевий тег {/block}. Він є необов'язковим.

На наступному рядку є посилання назад на список статей блогу, щоб користувач міг легко переміщатися між списком статей та однією конкретною. Оскільки ми використовуємо атрибут n:href, Nette саме подбає про генерацію посилань. Посилання вказує на дію default presenter'а Home (можна також написати n:href="Home:", оскільки дія з назвою default може бути пропущена, вона доповниться автоматично).

Третій рядок форматує виведення дати за допомогою фільтра, який ми вже знаємо.

Четвертий рядок відображає заголовок блогу в HTML-тегу <h1>. Цей тег містить атрибут, який ви, можливо, не знаєте (n:block="title"). Вгадаєте, що він робить? Якщо ви уважно читали попередню частину, то вже знаєте, що це n:атрибут. Це ще один їх приклад, який є еквівалентним до:

{block title}<h1>{$post->title}</h1>{/block}

Простіше кажучи, цей блок перевизначає блок з назвою title. Цей блок вже визначений у головному layout шаблоні (/app/Presentation/@layout.latte:11), і так само, як при перевизначенні методів в ООП, точно так само цей блок у головному шаблоні буде перевизначений. Отже, <title> сторінки тепер містить заголовок відображеного допису, і для цього нам знадобилося використати лише один простий атрибут n:block="title". Чудово, чи не так?

П'ятий і останній рядок шаблону відображає весь вміст одного конкретного допису.

Перевірка ID допису

Що станеться, якщо хтось змінить ID в URL і вставить якесь неіснуюче id? Ми повинні запропонувати користувачеві гарну помилку типу „сторінка не знайдена“. Трохи змінимо render-метод у PostPresenter:

public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Сторінку не знайдено');
	}

	$this->template->post = $post;
}

Якщо допис не може бути знайдений, викликом $this->error(...) ми відобразимо сторінку помилки 404 зі зрозумілим повідомленням. Зверніть увагу, що в режимі розробки (localhost) ви цю сторінку помилки не побачите. Замість неї з'явиться Tracy з деталями про виняток, що досить зручно для розробки. Якщо ми хочемо побачити обидва режими, достатньо лише змінити аргумент методу setDebugMode у файлі Bootstrap.php.

Підсумок

У нас є база даних з дописами та веб-застосунок, який має два представлення – перше відображає огляд усіх дописів, а друге – один конкретний допис.

OSZAR »