Commenti
Il blog è stato implementato, abbiamo scritto alcuni ottimi post e li abbiamo pubblicati tramite Adminer. Le persone leggono il blog e sono molto appassionate delle nostre idee. Ogni giorno riceviamo molte e-mail di elogio. Ma a cosa servono tutti questi apprezzamenti se li riceviamo solo nell'e-mail, in modo che nessun altro possa leggerli? Non sarebbe meglio se le persone potessero commentare direttamente sul blog, in modo che tutti gli altri possano leggere quanto siamo fantastici?
Rendiamo tutti gli articoli commentabili.
Creare una nuova tabella
Avviare nuovamente Adminer e creare una nuova tabella denominata comments
con queste colonne:
id
int, controllo autoincremento (AI)post_id
, una chiave esterna che fa riferimento alla tabellaposts
name
varchar, lunghezza 255email
varchar, lunghezza 255content
testocreated_at
timestamp
L'aspetto dovrebbe essere il seguente:
Non dimenticare di utilizzare la memorizzazione delle tabelle InnoDB e premere Salva.
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;
Modulo per i commenti
Per prima cosa, dobbiamo creare un modulo che permetta agli utenti di commentare la nostra pagina. Nette Framework offre un ottimo supporto per i moduli. Possono essere configurati in un presenter e resi in un template.
Nette Framework ha un concetto di componenti. Un componente è una classe o un pezzo di codice riutilizzabile,
che può essere collegato a un altro componente. Anche un presentatore è un componente. Ogni componente viene creato utilizzando
il component factory. Definiamo quindi il factory del modulo dei commenti in PostPresenter
.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Your name:')
->setRequired();
$form->addEmail('email', 'Email:');
$form->addTextArea('content', 'Comment:')
->setRequired();
$form->addSubmit('send', 'Publish comment');
return $form;
}
Spieghiamolo un po'. La prima riga crea una nuova istanza del componente Form
. I metodi seguenti allegano gli
input HTML alla definizione del form. ->addText
sarà reso come <input type=text name=name>
con
<label>Your name:</label>
. Come si sarà già intuito, ->addTextArea
aggiunge un input di
tipo <textarea>
e ->addSubmit
aggiunge un <input type=submit>
. Esistono
altri metodi del genere, ma questo è tutto ciò che occorre sapere al momento. Per saperne di più, consultare la documentazione.
Una volta definito il componente del modulo in un presentatore, possiamo renderlo (visualizzarlo) in un template. Per farlo,
posizionare il tag {control}
alla fine del template dei dettagli del post, in Post/show.latte
. Poiché
il nome del componente è commentForm
(deriva dal nome del metodo createComponentCommentForm
), il tag
avrà il seguente aspetto
...
<h2>Post new comment</h2>
{control commentForm}
Ora, se si controlla il dettaglio di un post, ci sarà un nuovo modulo per inserire i commenti.
Salvataggio nel database
Avete provato a inviare dei dati? Avrete notato che il modulo non esegue alcuna azione. È solo lì, bello da vedere e non fa nulla. Dobbiamo collegare un metodo di callback, che salverà i dati inviati.
Inserire la seguente riga prima della riga return
nel factory del componente commentForm
:
$form->onSuccess[] = $this->commentFormSucceeded(...);
Significa “dopo che il modulo è stato inviato con successo, chiamare il metodo commentFormSucceeded
del
presentatore 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('Thank you for your comment', 'success');
$this->redirect('this');
}
Dovremmo posizionarlo subito dopo il factory del componente commentForm
.
Il metodo new ha un parametro, che è l'istanza del form da inviare, creata dal component factory. Riceviamo i valori inviati
in $data
. Poi inseriamo i dati nella tabella del database comments
.
Ci sono altre due chiamate di metodo da spiegare. Il redirect reindirizza letteralmente alla pagina corrente. Si dovrebbe fare
ogni volta che il form è stato inviato, è valido e l'operazione di callback ha fatto ciò che doveva fare. Inoltre, quando si
reindirizza la pagina dopo l'invio del modulo, non si vedrà il noto messaggio
Would you like to submit the post data again?
, che a volte si può vedere nel browser. (In generale, dopo aver
inviato un modulo con il metodo POST
, si dovrebbe sempre reindirizzare l'utente a un'azione GET
).
Il messaggio flashMessage
serve a informare l'utente sul risultato di qualche operazione. Poiché si tratta di un
rinvio, il messaggio non può essere passato direttamente al template e reso. Quindi c'è questo metodo, che lo memorizza e lo
rende disponibile al successivo caricamento della pagina. I messaggi flash sono resi nel file predefinito
app/UI/@layout.latte
e si presentano così:
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Come già sappiamo, vengono passati automaticamente al template, quindi non occorre pensarci troppo, funziona e basta. Per maggiori dettagli, consultare la documentazione.
Rendering dei commenti
Questa è una delle cose che vi piacerà di più. Nette Database ha questa bella funzione chiamata Explorer. Ricordate che abbiamo creato le tabelle come InnoDB? Adminer ha creato le cosiddette chiavi esterne che ci risparmieranno un sacco di lavoro.
Nette Database Explorer utilizza le chiavi esterne per risolvere le relazioni tra le tabelle e, conoscendo le relazioni, può creare automaticamente le query per voi.
Come ricorderete, abbiamo passato la variabile $post
al modello in PostPresenter::renderShow()
e ora
vogliamo iterare tutti i commenti che hanno la colonna post_id
uguale alla nostra $post->id
. Per
farlo, basta richiamare $post->related('comments')
. È così semplice. Guardate il codice risultante.
public function renderShow(int $id): void
{
...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
E il modello:
...
<h2>Comments</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> said:</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Si noti l'attributo speciale n:tag-if
. Si sa già come funziona n: attributes
. Se si antepone
l'attributo tag-
, questo si avvolgerà solo intorno ai tag, non al loro contenuto. Questo permette di trasformare il
nome del commentatore in un link, se ha fornito la sua e-mail. Queste due righe hanno risultati identici:
<strong n:tag-if="$important"> Hello there! </strong>
{if $important}<strong>{/if} Hello there! {if $important}</strong>{/if}