Commenti
Il blog è stato messo online, abbiamo scritto qualche articolo molto interessante e lo abbiamo pubblicato con Adminer. La gente legge il nostro blog e ne è entusiasta. Ogni giorno riceviamo tante email di elogio. Ma a che serve tutto questo elogio se resta solo nella nostra email e nessun altro può leggerlo? Non sarebbe meglio se i lettori potessero commentare direttamente gli articoli, così che tutti vedano quanto siamo bravi?
Realizziamo i commenti.
Creazione di una nuova tabella
Avviamo di nuovo Adminer e creiamo una tabella comments con queste colonne:
idint, spuntate autoincrement (AI)post_id, chiave esterna che punta alla tabellapostsnamevarchar, lunghezza 255emailvarchar, lunghezza 255contenttextcreated_attimestamp
La tabella dovrebbe apparire più o meno così:

Ricordatevi di usare di nuovo il motore di storage InnoDB.
CREATE TABLE `comments` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`post_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`content` text NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
) ENGINE=InnoDB CHARSET=utf8;
Form per commentare
Per prima cosa dobbiamo creare un form che permetta agli utenti di commentare gli articoli. Il Nette Framework ha un ottimo supporto per i form. Possiamo configurarli nel presenter e renderizzarli nel template.
Il Nette Framework usa il concetto di componenti. Un componente è una classe o un pezzo di codice
riutilizzabile che si può agganciare a un altro componente. Anche un presenter è un componente. Ogni componente si crea con un
metodo factory. Creiamo nel PostPresenter una factory per il form dei commenti.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Il vostro nome:')
->setRequired();
$form->addEmail('email', 'Email:');
$form->addTextArea('content', 'Commento:')
->setRequired();
$form->addSubmit('send', 'Pubblica il commento');
return $form;
}
Spieghiamolo brevemente. La prima riga crea una nuova istanza del componente Form. I metodi successivi agganciano
gli input HTML alla definizione del form. ->addText viene renderizzato come
<input type="text" name="name"> con un <label>Il vostro nome:</label>. Come
immaginate, ->addTextArea aggancia una <textarea> e ->addSubmit aggiunge un
<input type="submit">. Ci sono molti altri metodi simili, ma per ora questi bastano. Potete saperne di più nella documentazione.
Una volta definito il componente form nel presenter, possiamo renderizzarlo (mostrarlo) nel template. Per farlo mettete il tag
{control} alla fine del template che mostra il singolo articolo, in Post/show.latte. Poiché il
componente si chiama commentForm (derivato dal nome del metodo createComponentCommentForm), il tag
sarà così:
...
<h2>Pubblica un nuovo commento</h2>
{control commentForm}
Se ora aprite la pagina di dettaglio di un articolo, alla fine vedrete il nuovo form per i commenti.
Salvataggio nel database
Avete provato a compilare e inviare il form? Avrete notato che il form non fa ancora nulla. Dobbiamo agganciare un metodo di callback che salvi i dati inviati.
Aggiungete questa riga prima dell'istruzione return nel metodo factory del componente
commentForm:
$form->onSuccess[] = $this->commentFormSucceeded(...);
Questa riga significa “dopo l'invio riuscito del form chiama il metodo commentFormSucceeded del presenter
corrente”. Questo metodo non esiste ancora, quindi creiamolo:
private function commentFormSucceeded(\stdClass $data): void
{
$id = $this->getParameter('id');
$this->database->table('comments')->insert([
'post_id' => $id,
'name' => $data->name,
'email' => $data->email,
'content' => $data->content,
]);
$this->flashMessage('Grazie per il vostro commento', 'success');
$this->redirect('this');
}
Mettetelo subito dopo la factory del componente commentForm.
Il nuovo metodo ha un argomento, l'istanza del form inviato, creata dalla factory del componente. I valori inviati li
riceviamo in $data. E poi inseriamo i dati nella tabella del database comments.
Ci sono altre due chiamate di metodo da spiegare. Il metodo redirect('this') reindirizza letteralmente alla pagina
corrente. Dovreste farlo ogni volta che un form viene inviato con successo e l'operazione del callback è terminata. Reindirizzare
dopo l'invio del form previene anche il noto messaggio del browser “Volete inviare di nuovo i dati del post?”. (In generale,
dopo aver inviato un form con il metodo POST dovreste sempre reindirizzare a un'azione GET.)
Il metodo flashMessage serve a informare l'utente sul risultato di un'operazione. Poiché stiamo reindirizzando,
il messaggio non si può semplicemente passare al template e renderizzarlo subito. Questo metodo salva il messaggio e lo rende
disponibile al caricamento della pagina successiva. I messaggi flash vengono renderizzati nel template di layout principale
app/Presentation/@layout.latte e appaiono così:
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Come già sappiamo, i messaggi flash vengono passati automaticamente al template, quindi non dobbiamo pensarci molto: funziona e basta. Per maggiori dettagli visitate la documentazione.
Visualizzazione dei commenti
Questa è una di quelle funzionalità che amerete. Nette Database ha un'ottima funzione chiamata Explorer. Ricordate come abbiamo creato di proposito le tabelle del database con il motore InnoDB? Adminer ha creato le cosiddette chiavi esterne, che ci risparmiano molto lavoro.
Nette Database Explorer usa queste chiavi esterne per risolvere le relazioni tra le tabelle. Conoscendo queste relazioni, sa creare automaticamente le query al database per voi.
Come ricorderete, nel PostPresenter::renderShow() abbiamo passato al template la variabile $post. Ora
vogliamo iterare su tutti i commenti la cui colonna post_id corrisponde al nostro $post->id. Lo
otteniamo chiamando $post->related('comments'). Sì, è così semplice. Guardiamo il codice finale:
public function renderShow(int $id): void
{
// ...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
E il template:
...
<h2>Commenti</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> ha scritto:</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Notate l'attributo particolare n:tag-if. Sapete già come funzionano gli n:attributi. Se anteponete
all'attributo tag-, la funzionalità si applica solo al tag HTML stesso, non al suo contenuto. Questo ci permette di
rendere il nome di chi commenta un link solo se ha indicato la propria email. Queste due righe producono lo stesso risultato:
<strong n:tag-if="$important"> Ciao a tutti! </strong>
{if $important}<strong>{/if} Ciao a tutti! {if $important}</strong>{/if}