Pagina di un singolo articolo

Aggiungiamo al nostro blog un'altra pagina, che mostrerà il contenuto di un singolo articolo.

Dobbiamo creare un nuovo metodo render che ottenga un articolo specifico e lo passi al template. Mettere questo metodo in HomePresenter non è molto pulito, perché si tratta di un singolo articolo e non della pagina principale. Creiamo quindi una nuova classe PostPresenter e mettiamola in app/Presentation/Post/. Anche questo presenter avrà bisogno della connessione al database, quindi aggiungeremo il costruttore che la richiede.

Il PostPresenter può apparire così:

<?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);
	}
}

Non dobbiamo dimenticare di indicare il namespace corretto App\Presentation\Post, che dipende dalla configurazione della mappatura dei presenter.

Il metodo renderShow richiede un argomento, l'ID dell'articolo da mostrare. Carica poi l'articolo dal database e lo passa al template.

Nel template Home/default.latte aggiungiamo un link all'azione Post:show:

...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...

Il tag {link} genera un indirizzo URL che punta all'azione Post:show. Passa inoltre come argomento l'ID dell'articolo.

La stessa cosa si può scrivere in modo conciso con un n:attributo:

...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...

L'attributo n:href è analogo al tag {link}.

Il template per l'azione Post:show però non esiste ancora. Possiamo provare ad aprire il link a questo articolo. Tracy mostrerà un errore, perché il template Post/show.latte non esiste ancora. Se vedete un messaggio di errore diverso, probabilmente dovete attivare mod_rewrite sul vostro server web.

Creiamo quindi Post/show.latte con questo contenuto:

{block content}

<p><a n:href="Home:default">← torna all'elenco degli articoli</a></p>

<div class="date">{$post->created_at|date:'j. n. Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>

Passiamo ora in rassegna le singole parti del template.

La prima riga apre la definizione di un blocco chiamato “content”, proprio come nella pagina principale. Questo blocco verrà di nuovo mostrato dentro il template di layout principale. Come vedete, manca il tag di chiusura {/block}. È facoltativo.

La seconda riga offre un link di ritorno all'elenco degli articoli del blog, così l'utente può muoversi facilmente tra l'elenco e un singolo articolo. Usiamo di nuovo l'attributo n:href, quindi Nette si occuperà di generare l'URL. Il link punta all'azione default del presenter Home (potreste anche scrivere n:href="Home:", perché il nome dell'azione default si può omettere, viene aggiunto automaticamente).

La terza riga formatta la data con un filtro che già conosciamo.

La quarta riga mostra il titolo dell'articolo dentro un tag HTML <h1>. Questo tag contiene un attributo che forse non conoscete (n:block="title"). Indovinate che cosa fa? Se avete letto attentamente la sezione precedente, sapete già che è un n:attributo. È un altro esempio, equivalente a:

{block title}<h1>{$post->title}</h1>{/block}

In parole semplici, questo blocco ridefinisce il blocco chiamato title. Questo blocco è già definito nel template layout principale (/app/Presentation/@layout.latte:11) e, proprio come la ridefinizione dei metodi nell'OOP, questo blocco sovrascrive quello del template principale. Il <title> della pagina conterrà quindi ora il titolo dell'articolo mostrato, e ci è bastato questo semplice attributo n:block="title". Fantastico, vero?

La quinta e ultima riga del template mostra il contenuto completo del singolo articolo.

Verifica dell'ID dell'articolo

Che cosa succede se qualcuno modifica l'ID nell'URL e inserisce un id inesistente? Dovremmo mostrare all'utente un bel messaggio di errore “pagina non trovata”. Modifichiamo leggermente il metodo render in PostPresenter:

public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Articolo non trovato');
	}

	$this->template->post = $post;
}

Se l'articolo non si trova, la chiamata $this->error(...) mostrerà una pagina 404 con un messaggio comprensibile. Notate che nell'ambiente di sviluppo (su localhost) questa pagina di errore non la vedrete. Al suo posto Tracy mostrerà l'eccezione con tutti i dettagli, il che è molto comodo per lo sviluppo. Se volete provare entrambe le modalità, basta cambiare l'argomento passato al metodo setDebugMode in Bootstrap.php.

Abbiamo un database con gli articoli e un'applicazione web con due viste: la prima mostra una panoramica di tutti gli articoli, la seconda mostra un singolo articolo.