Страница поста
Теперь мы создадим еще одну страницу блога, которая будет отображать один конкретный пост.
Нам нужно создать новый метод render, который получит одну конкретную
статью и передаст ее в шаблон. Иметь этот метод в HomePresenter не
очень красиво, потому что мы говорим о статье, а не о главной странице.
Поэтому создадим PostPresenter в app/Presentation/Post/. Этот презентер
также должен подключаться к базе данных, поэтому здесь снова напишем
конструктор, который будет требовать подключение к базе данных.
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, которое подчиняется настройке сопоставления
презентеров.
Метод 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 презентера 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.
Резюме
У нас есть база данных с постами и веб-приложение, которое имеет два представления – первое отображает обзор всех постов, а второе отображает один конкретный пост.