Strona pojedynczego wpisu
Dodajmy do naszego bloga kolejną stronę, która będzie wyświetlać treść jednego konkretnego wpisu.
Musimy utworzyć nową metodę render, która pobierze jeden konkretny wpis i przekaże go do szablonu. Umieszczenie tej
metody w HomePresenter nie jest zbyt eleganckie, bo chodzi o pojedynczy wpis, a nie o stronę główną. Utwórzmy
więc nową klasę PostPresenter i umieśćmy ją w app/Presentation/Post/. Ten presenter też będzie
potrzebował połączenia z bazą danych, więc dodamy konstruktor, który będzie tego połączenia wymagał.
PostPresenter mógłby wyglądać tak:
<?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);
}
}
Nie możemy zapomnieć o podaniu poprawnej przestrzeni nazw App\Presentation\Post, która zależy od konfiguracji
mapowania presenterów.
Metoda renderShow wymaga jednego argumentu: ID wpisu, który ma zostać wyświetlony. Następnie wczytuje wpis
z bazy danych i przekazuje go do szablonu.
W szablonie Home/default.latte dodajemy odnośnik do akcji Post:show:
...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...
Tag {link} generuje adres URL wskazujący na akcję Post:show. Przekazuje też jako argument
ID wpisu.
To samo można zapisać zwięźle za pomocą n:atrybutu:
...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...
Atrybut n:href jest podobny do tagu {link}.
Szablon dla akcji Post:show jednak jeszcze nie istnieje. Możemy spróbować otworzyć odnośnik do tego wpisu. Tracy pokaże błąd, bo szablon Post/show.latte jeszcze nie istnieje. Jeśli
widzisz inny komunikat o błędzie, prawdopodobnie musisz włączyć na swoim serwerze webowym mod_rewrite.
Utworzymy więc Post/show.latte z taką treścią:
{block content}
<p><a n:href="Home:default">← powrót do listy wpisów</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>
Przejrzyjmy teraz poszczególne części szablonu.
Pierwsza linia zaczyna definicję bloku o nazwie “content”, tak samo jak na stronie głównej. Blok ten znów zostanie
wyświetlony w ramach głównego szablonu layoutu. Jak widzisz, brakuje tagu końcowego {/block}. Jest on
opcjonalny.
Druga linia daje odnośnik powrotny do listy wpisów blogowych, pozwalając użytkownikowi łatwo poruszać się między listą
wpisów a konkretnym wpisem. Znów używamy atrybutu n:href, więc o wygenerowanie URL zadba Nette. Odnośnik
wskazuje na akcję default presentera Home (mógłbyś napisać też n:href="Home:", bo
nazwę akcji default można pominąć, zostanie dodana automatycznie).
Trzecia linia formatuje datę filtrem, który już znamy.
Czwarta linia wyświetla tytuł wpisu blogowego wewnątrz tagu HTML <h1>. Tag ten zawiera atrybut,
którego możesz nie rozpoznawać (n:block="title"). Zgadniesz, co robi? Jeśli uważnie przeczytałeś poprzednią
sekcję, już wiesz, że to n:atrybut. To kolejny przykład, równoważny z:
{block title}<h1>{$post->title}</h1>{/block}
Upraszczając, blok ten redefiniuje blok o nazwie title. Blok ten jest już zdefiniowany w głównym szablonie
layoutu (/app/Presentation/@layout.latte:11) i podobnie jak nadpisywanie metod w OOP nadpisuje ten
z szablonu głównego. <title> strony będzie więc teraz zawierać tytuł wyświetlanego wpisu, a wystarczył
nam do tego prosty atrybut n:block="title". Świetne, prawda?
Piąta i ostatnia linia szablonu wyświetla pełną treść konkretnego wpisu.
Sprawdzanie ID wpisu
Co się stanie, jeśli ktoś zmieni ID w URL i wstawi nieistniejące id? Powinniśmy pokazać użytkownikowi
ładny błąd “strona nie znaleziona”. Zmodyfikujmy nieco metodę render w PostPresenter:
public function renderShow(int $id): void
{
$post = $this->database
->table('posts')
->get($id);
if (!$post) {
$this->error('Post not found');
}
$this->template->post = $post;
}
Jeśli wpisu nie da się znaleźć, wywołanie $this->error(...) wyświetli stronę 404 ze zrozumiałym
komunikatem. Zwróć uwagę, że w środowisku deweloperskim (na localhoście) tej strony błędu nie zobaczysz. Zamiast niej
Tracy pokaże wyjątek z pełnymi szczegółami, co jest przy tworzeniu całkiem wygodne. Jeśli chcesz przetestować oba tryby,
po prostu zmień argument przekazywany metodzie setDebugMode w Bootstrap.php.
Podsumowanie
Mamy bazę danych z wpisami i aplikację webową z dwoma widokami: pierwszy wyświetla przegląd wszystkich wpisów, a drugi jeden konkretny wpis.