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

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

Нам потрібно створити новий 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.

Підсумок

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