Commenti
Abbiamo caricato il blog sul webserver e pubblicato alcuni post molto interessanti usando Adminer. Le persone leggono il nostro blog e ne sono molto entusiaste. Riceviamo ogni giorno molte email con complimenti. Ma a cosa serve tutta questa lode se la abbiamo solo nelle email e nessuno può leggerla? Sarebbe meglio se il lettore potesse commentare direttamente l'articolo, così che tutti possano leggere quanto siamo fantastici.
Programmiamo quindi i commenti.
Creazione di una nuova tabella
Avviamo Adminer e creiamo una tabella comments
con le seguenti colonne:
id
int, selezioniamo autoincrement (AI)post_id
, chiave esterna che fa riferimento alla tabellaposts
name
varchar, lunghezza 255email
varchar, lunghezza 255content
textcreated_at
timestamp
La tabella dovrebbe quindi apparire più o meno così:

Non dimenticate di usare nuovamente lo storage InnoDB.
CREATE TABLE `comments` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`post_id` int(11) NOT NULL,
`name` varchar(250) NOT NULL,
`email` varchar(250) 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
Prima di tutto, dobbiamo creare un form che permetta agli utenti di commentare i post. Nette Framework ha un supporto straordinario per i form. Possiamo configurarli nel presenter e renderizzarli nel template.
Nette Framework utilizza il concetto di componenti. Un componente è una classe riutilizzabile, o una parte di
codice, che può essere allegata a un altro componente. Anche il presenter è un componente. Ogni componente viene creato tramite
una factory. Creeremo quindi una factory per il form dei commenti nel presenter PostPresenter
.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Nome:')
->setRequired();
$form->addEmail('email', 'E-mail:');
$form->addTextArea('content', 'Commento:')
->setRequired();
$form->addSubmit('send', 'Pubblica commento');
return $form;
}
Spieghiamolo di nuovo un po'. La prima riga crea una nuova istanza del componente Form
. I metodi successivi
aggiungono input HTML alla definizione di questo form. ->addText()
verrà renderizzato come
<input type=text name=name>
con <label>Nome:</label>
. Come probabilmente avrete già
indovinato correttamente, ->addTextArea()
verrà renderizzato come <textarea>
e
->addSubmit()
come <input type=submit>
. Esistono molti altri metodi simili, ma questi per ora
sono sufficienti per questo form. Potete leggere di più nella documentazione.
Se il form è già definito nel presenter, possiamo renderizzarlo (visualizzarlo) nel template. Lo facciamo inserendo il tag
{control}
alla fine del template che renderizza un singolo post, in app/Presentation/Post/show.latte
.
Poiché il componente si chiama commentForm
(il nome deriva dal nome del metodo
createComponentCommentForm
), il tag apparirà così:
...
<h2>Inserisci un nuovo post</h2>
{control commentForm}
Se ora visualizzate la pagina con i dettagli del post, alla fine vedrete il nuovo form per i commenti.
Salvataggio nel database
Avete già provato a compilare e inviare il form? Probabilmente avrete notato che il form in realtà non fa nulla. Dobbiamo collegare un metodo di callback che salvi i dati inviati.
Nella factory createComponentCommentForm
, sulla riga prima di return
, inseriamo la
seguente riga:
$form->onSuccess[] = $this->commentFormSucceeded(...);
La scrittura precedente significa “dopo l'invio riuscito del form, chiama il metodo commentFormSucceeded
del
presenter corrente”. Questo metodo, tuttavia, non esiste ancora. Creiamolo quindi:
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 commento', 'success');
$this->redirect('this');
}
Posizioniamo questo metodo subito dopo la factory del form commentForm
.
Questo nuovo metodo ha un argomento, che è un'istanza del form che è stato inviato – creato dalla factory. Otteniamo
i valori inviati in $data
. E successivamente salviamo i dati nella tabella del database comments
.
Ci sono ancora altri due metodi che meritano di essere spiegati. Il metodo redirect('this')
reindirizza
letteralmente alla pagina corrente. È opportuno farlo dopo ogni invio di form, se conteneva dati validi e il callback ha eseguito
l'operazione come previsto. E anche se reindirizziamo la pagina dopo l'invio del form, non vedremo il ben noto messaggio
Vuoi inviare nuovamente i dati del form?
, che a volte possiamo vedere nel browser. (In generale, dopo l'invio di un
form con il metodo POST
dovrebbe sempre seguire un reindirizzamento a un'azione GET
.) Abbiamo aggiunto
#comments
per reindirizzare direttamente alla sezione dei commenti.
Il metodo flashMessage()
serve per informare l'utente sul risultato di qualche operazione. Poiché stiamo
reindirizzando, il messaggio non può essere semplicemente passato al template e renderizzato. Per questo esiste questo metodo,
che salva questo messaggio e lo rende disponibile al successivo caricamento della pagina. I messaggi flash vengono renderizzati
nel template 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 automaticamente passati al template tramite la variabile $flashes
,
quindi non dobbiamo pensarci molto, semplicemente funziona. Per maggiori informazioni visitate la documentazione.
Renderizzazione dei commenti
Questa è una di quelle cose che semplicemente adorerete. Nette Database ha una funzione fantastica chiamata Explorer. Ricordate ancora che abbiamo volutamente creato le tabelle nel database usando lo storage InnoDB? Adminer ha così creato qualcosa chiamato chiavi esterne, che ci risparmierà molto lavoro.
Nette Database Explorer utilizza le chiavi esterne per risolvere le relazioni reciproche tra le tabelle e, grazie alla conoscenza di queste relazioni, può creare automaticamente query sul database.
Come sicuramente ricorderete, abbiamo passato la variabile $post
al template tramite il metodo
PostPresenter::renderShow()
e ora vogliamo iterare su tutti i commenti che hanno il valore della colonna
post_id
uguale a $post->id
. Possiamo ottenere ciò chiamando
$post->related('comments')
. Sì, è così semplice. Diamo un'occhiata al codice risultante:
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 speciale n:tag-if
. Sapete già come funzionano gli n:attributi
. Se aggiungete il
prefisso tag-
, la funzionalità viene applicata solo al tag HTML, non al suo contenuto. Questo ci permette di
trasformare il nome del commentatore in un link solo nel caso in cui abbia fornito la sua email. Queste due righe sono
equivalenti:
<strong n:tag-if="$important"> Buongiorno! </strong>
{if $important}<strong>{/if} Buongiorno! {if $important}</strong>{/if}