Страница отдельной записи
Давайте добавим в наш блог еще одну страницу, на которой будет отображаться содержимое одной конкретной записи блога.
Нам нужно создать новый метод 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
для
нашего презентера. Это зависит от presenter mapping.
Метод 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
.
Подведём итог
У нас есть база данных с записями блога и веб-приложение с двумя представлениями: первое отображает сводку всех последних записей, а второе — одну конкретную запись.