Страница поста

Теперь мы создадим еще одну страницу блога, которая будет отображать один конкретный пост.

Нам нужно создать новый метод 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.

Резюме

У нас есть база данных с постами и веб-приложение, которое имеет два представления – первое отображает обзор всех постов, а второе отображает один конкретный пост.