Страница поста
Теперь мы создадим еще одну страницу блога, которая будет отображать один конкретный пост.
Нам нужно создать новый метод 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
.
Резюме
У нас есть база данных с постами и веб-приложение, которое имеет два представления – первое отображает обзор всех постов, а второе отображает один конкретный пост.