Strona z postem
Teraz stworzymy kolejną stronę bloga, która będzie wyświetlać jeden konkretny post.
Musimy stworzyć nową metodę render, która pobierze jeden konkretny artykuł i przekaże go do szablonu. Umieszczenie tej
metody w HomePresenter
nie jest zbyt eleganckie, ponieważ mówimy o artykule, a nie o stronie głównej. Stwórzmy
więc PostPresenter
w app/Presentation/Post/
. Ten presenter również potrzebuje połączenia z bazą
danych, więc ponownie napiszemy konstruktor, który będzie wymagał połączenia z bazą danych.
PostPresenter
mógłby więc 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 podlega ustawieniom mapowania presenterów.
Metoda renderShow
wymaga jednego argumentu – ID jednego konkretnego artykułu, który ma być wyświetlony.
Następnie ten artykuł wczytuje z bazy danych i przekazuje go do szablonu.
Do szablonu Home/default.latte
wstawimy link do akcji Post:show
.
...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...
Znacznik {link}
generuje adres URL, który wskazuje na akcję Post:show
. Przekazuje również ID
posta jako argument.
To samo możemy zapisać skrótowo za pomocą n:atrybutu:
...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...
Atrybut n:href
jest odpowiednikiem tagu {link}
.
Dla akcji Post:show
jednak jeszcze nie istnieje szablon. Możemy spróbować otworzyć link do tego posta. Tracy wyświetli błąd, ponieważ szablon Post/show.latte
jeszcze nie
istnieje. Jeśli widzisz inny komunikat błędu, prawdopodobnie będziesz musiał włączyć mod_rewrite
na
serwerze WWW.
Stworzymy więc szablon Post/show.latte
z następującą zawartością:
{block content}
<p><a n:href="Home:default">← wróć do listy postów</a></p>
<div class="date">{$post->created_at|date:'j F Y'}</div>
<h1 n:block="title">{$post->title}</h1>
<div class="post">{$post->content}</div>
Teraz przejdziemy przez poszczególne części szablonu.
Pierwsza linia zaczyna definicję bloku o nazwie “content”, tak samo jak było to na stronie głównej. Ten blok zostanie
ponownie wyświetlony w głównym szablonie. Jak widzisz, brakuje końcowego znacznika {/block}
. Jest on bowiem
opcjonalny.
W następnej linii znajduje się link powrotny do listy artykułów bloga, dzięki czemu użytkownik może łatwo poruszać
się między listą artykułów a jednym konkretnym. Ponieważ używamy atrybutu n:href
, Nette samo zadba
o generowanie linków. Link wskazuje na akcję default
presentera Home
(możemy również napisać
n:href="Home:"
, ponieważ akcja o nazwie default
może być pominięta, zostanie uzupełniona
automatycznie).
Trzecia linia formatuje wyświetlanie daty za pomocą filtra, który już znamy.
Czwarta linia wyświetla tytuł bloga w tagu HTML <h1>
. Ten tag zawiera atrybut, którego być może
nie znasz (n:block="title"
). Zgadniesz, co robi? Jeśli czytałeś poprzednią część uważnie, już wiesz, że
jest to n:atrybut
. To jest ich kolejny przykład, który jest równoważny z:
{block title}<h1>{$post->title}</h1>{/block}
Mówiąc prościej, ten blok przedefiniowuje blok o nazwie title
. Ten blok jest już zdefiniowany w głównym
szablonie layout (/app/Presentation/@layout.latte:11
) i tak jak w przypadku nadpisywania metod w OOP,
dokładnie tak samo ten blok w głównym szablonie zostanie nadpisany. Tak więc <title>
strony teraz zawiera
tytuł wyświetlanego posta, a wystarczyło nam do tego użyć tylko jednego prostego atrybutu n:block="title"
.
Świetnie, prawda?
Piąta i ostatnia linia szablonu wyświetla całą treść jednego konkretnego posta.
Sprawdzanie ID posta
Co się stanie, jeśli ktoś zmieni ID w URL i wstawi jakieś nieistniejące id
? Powinniśmy zaoferować
użytkownikowi ładny błąd typu “strona nie została znaleziona”. Zmodyfikujemy więc trochę metodę render w
PostPresenter
:
public function renderShow(int $id): void
{
$post = $this->database
->table('posts')
->get($id);
if (!$post) {
$this->error('Strona nie została znaleziona');
}
$this->template->post = $post;
}
Jeśli post nie może zostać znaleziony, wywołaniem $this->error(...)
wyświetlimy stronę błędu
404 z czytelnym komunikatem. Uważaj na to, że w trybie deweloperskim (localhost) tej strony błędu nie zobaczysz. Zamiast
tego pokaże się Tracy ze szczegółami wyjątku, co jest dość wygodne podczas rozwoju. Jeśli chcemy wyświetlić oba tryby,
wystarczy zmienić argument metody setDebugMode
w pliku Bootstrap.php
.
Podsumowanie
Mamy bazę danych z postami i aplikację internetową, która ma dwa widoki – pierwszy wyświetla przegląd wszystkich postów, a drugi wyświetla jeden konkretny post.