Nette Documentation Preview

syntax
Сторінка окремого запису
************************

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


Нам потрібно створити новий метод render, який буде отримувати один конкретний запис блогу і передавати його в шаблон. Мати це подання в `HomePresenter` не дуже приємно, тому що йдеться про запис у блозі, а не про головну сторінку. Отже, давайте створимо новий клас `PostPresenter` і помістимо його в `app/UI/Post/`. Йому знадобиться з'єднання з базою даних, тому знову помістіть туди код *впровадження залежності*.

`PostPresenter` має виглядати наступним чином:

```php .{file:app/UI/Post/PostPresenter.php}
<?php
namespace App\UI\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\UI\Post` для нашого презентера. Це залежить від [mapping 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/UI/Home/default.latte}
...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...
```

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


Те ж саме ми можемо написати коротко, використовуючи n:attribute:

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

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



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

Тому ми створимо `Post/show.latte` з таким змістом:

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

<p><a n:href="Home:default">← вернуться к списку постов</a></p>

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

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

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

Розглянемо деякі моменти.

Перший рядок починає визначення *іменованого блоку* під назвою `content`, які ми бачили раніше. Він буде відображатися в *шаблоні макета*.

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

Третій рядок форматує тимчасову мітку публікації за допомогою фільтра `date`, як ми вже знаємо.

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

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

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

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


Перевірка ідентифікатора поста .[#toc-checking-post-id]
=======================================================

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

```php .{file:app/UI/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 з красивим і зрозумілим повідомленням. Зверніть увагу, що в режимі розробки ви не побачите сторінку помилки. Натомість Tracy покаже виняток із повною інформацією, що досить зручно для розробки. Ви можете перевірити обидва режими, просто змінивши значення, що передається в `setDebugMode` на `Bootstrap.php`.


Підіб'ємо підсумок .[#toc-summary]
==================================

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

{{priority: -1}}
{{sitename: Быстрый старт с Nette}}

Сторінка окремого запису

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

Нам потрібно створити новий метод render, який буде отримувати один конкретний запис блогу і передавати його в шаблон. Мати це подання в HomePresenter не дуже приємно, тому що йдеться про запис у блозі, а не про головну сторінку. Отже, давайте створимо новий клас PostPresenter і помістимо його в app/UI/Post/. Йому знадобиться з'єднання з базою даних, тому знову помістіть туди код впровадження залежності.

PostPresenter має виглядати наступним чином:

<?php
namespace App\UI\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\UI\Post для нашого презентера. Це залежить від mapping 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:attribute:

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

Атрибут n:href аналогічний тегу {link}.

Шаблон для дії Post:show ще не існує. Ми можемо відкрити посилання на цей пост. Tracy покаже помилку про те, що app/UI/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:'j.m.Y'}</div>

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

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

Розглянемо деякі моменти.

Перший рядок починає визначення іменованого блоку під назвою content, які ми бачили раніше. Він буде відображатися в шаблоні макета.

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

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

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

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

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

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

Перевірка ідентифікатора поста

Що станеться, якщо хтось змінить URL і вставить неіснуючий id? Ми повинні надати користувачеві красиву сторінку помилки „сторінку не знайдено“. Давайте оновимо метод render у файлі 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 з красивим і зрозумілим повідомленням. Зверніть увагу, що в режимі розробки ви не побачите сторінку помилки. Натомість Tracy покаже виняток із повною інформацією, що досить зручно для розробки. Ви можете перевірити обидва режими, просто змінивши значення, що передається в setDebugMode на Bootstrap.php.

Підіб'ємо підсумок

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