Commentaires
Le blog est déployé, nous avons écrit quelques articles très intéressants et les avons publiés à l'aide d'Adminer. Les gens lisent notre blog et s'en enthousiasment. Nous recevons chaque jour de nombreux e-mails élogieux. Mais à quoi bon tous ces éloges s'ils restent dans notre boîte mail et que personne d'autre ne peut les lire ? Ne serait-il pas préférable que les lecteurs puissent commenter directement les articles, pour que tout le monde voie à quel point nous sommes formidables ?
Implémentons les commentaires.
Créer une nouvelle table
Relançons Adminer et créons une table comments avec ces colonnes :
idint, cochez autoincrement (AI)post_id, clé étrangère référençant la tablepostsnamevarchar, longueur 255emailvarchar, longueur 255contenttextcreated_attimestamp
La table devrait ressembler à ceci :

N'oubliez pas d'utiliser de nouveau le moteur de stockage 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;
Formulaire de commentaire
Nous devons d'abord créer un formulaire permettant aux utilisateurs de commenter les articles. Nette Framework prend excellemment en charge les formulaires. Nous pouvons les configurer dans le presenter et les rendre dans le template.
Nette Framework utilise la notion de composants. Un composant est une classe ou un morceau de code réutilisable
qui peut être rattaché à un autre composant. Même un presenter est un composant. Chaque composant est créé par une méthode
fabrique. Créons dans PostPresenter une factory pour le formulaire de commentaire.
protected function createComponentCommentForm(): Form
{
$form = new Form; // c'est-à-dire Nette\Application\UI\Form
$form->addText('name', 'Votre nom :')
->setRequired();
$form->addEmail('email', 'E-mail :');
$form->addTextArea('content', 'Commentaire :')
->setRequired();
$form->addSubmit('send', 'Publier le commentaire');
return $form;
}
Expliquons cela brièvement. La première ligne crée une nouvelle instance du composant Form. Les méthodes
suivantes rattachent des champs HTML à la définition du formulaire. ->addText se rend en
<input type="text" name="name"> avec un <label>Votre nom :</label>. Comme vous vous en
doutez, ->addTextArea rattache un <textarea> et ->addSubmit ajoute un
<input type="submit">. Il existe beaucoup de méthodes semblables, mais celles-ci suffisent pour l'instant.
Vous pouvez en apprendre davantage dans la documentation.
Une fois le composant formulaire défini dans le presenter, nous pouvons le rendre (l'afficher) dans le template. Pour cela,
placez la balise {control} à la fin du template qui affiche un article isolé, dans Post/show.latte.
Comme le composant s'appelle commentForm (dérivé du nom de la méthode createComponentCommentForm), la
balise ressemblera à ceci :
...
<h2>Publier un nouveau commentaire</h2>
{control commentForm}
Désormais, si vous consultez la page de détail d'un article, vous verrez le nouveau formulaire de commentaire à la fin.
Enregistrer dans la base de données
Avez-vous essayé de remplir et d'envoyer le formulaire ? Vous avez peut-être remarqué qu'il ne fait encore rien. Nous devons y rattacher une méthode de rappel qui enregistrera les données soumises.
Ajoutez la ligne suivante avant l'instruction return de la méthode fabrique du composant
commentForm :
$form->onSuccess[] = $this->commentFormSucceeded(...);
Cette ligne signifie “après l'envoi réussi du formulaire, appelle la méthode commentFormSucceeded du
presenter courant”. Cette méthode n'existe pas encore, créons-la donc :
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('Merci pour votre commentaire', 'success');
$this->redirect('this');
}
Vous devriez la placer juste après la factory du composant commentForm.
La nouvelle méthode a un argument, qui est l'instance du formulaire envoyé, créée par la factory du composant. Nous
recevons les valeurs soumises dans $data. Puis nous insérons les données dans la table comments de
la base.
Il reste deux appels de méthode à expliquer. La méthode redirect('this') redirige littéralement vers la page
courante. Vous devriez le faire chaque fois qu'un formulaire est envoyé avec succès et que l'opération de rappel s'est
terminée. Rediriger après l'envoi d'un formulaire évite aussi le fameux message “Voulez-vous renvoyer les données du
formulaire ?” du navigateur. (De façon générale, après l'envoi d'un formulaire par la méthode POST, vous
devriez toujours rediriger vers une action GET.)
La méthode flashMessage sert à informer l'utilisateur du résultat d'une opération. Comme nous redirigeons, le
message ne peut pas être simplement passé au template et rendu directement. Cette méthode enregistre le message et le rend
disponible au chargement suivant de la page. Les messages flash sont rendus dans le template de layout principal
app/Presentation/@layout.latte, ainsi :
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Comme nous le savons déjà, les messages flash sont automatiquement passés au template, nous n'avons donc pas à y réfléchir : cela fonctionne tout seul. Pour plus de détails, consultez la documentation.
Rendre les commentaires
C'est l'une de ces fonctionnalités que vous allez tout simplement adorer. Nette Database possède une excellente fonctionnalité appelée Explorer. Vous vous souvenez que nous avons créé exprès les tables avec le moteur InnoDB ? Adminer a créé ce qu'on appelle des clés étrangères, qui nous épargnent beaucoup de travail.
Nette Database Explorer utilise ces clés étrangères pour résoudre les relations entre les tables. Connaissant ces relations, il peut créer automatiquement les requêtes de base de données pour vous.
Comme vous vous en souvenez peut-être, nous avons passé la variable $post au template dans
PostPresenter::renderShow(). Nous voulons maintenant parcourir tous les commentaires dont la colonne
post_id correspond à notre $post->id. Nous pouvons y parvenir en appelant
$post->related('comments'). Oui, c'est aussi simple que cela. Regardons le code final :
public function renderShow(int $id): void
{
// ...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
Et le template :
...
<h2>Commentaires</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> a dit :</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Remarquez l'attribut particulier n:tag-if. Vous savez déjà comment fonctionnent les n:attributs. Si
vous préfixez l'attribut par tag-, la fonctionnalité ne s'applique qu'à la balise HTML elle-même, pas à son
contenu. Cela nous permet de ne faire du nom du commentateur un lien que s'il a fourni son e-mail. Ces deux lignes produisent des
résultats identiques :
<strong n:tag-if="$important"> Bonjour ! </strong>
{if $important}<strong>{/if} Bonjour ! {if $important}</strong>{/if}