Commentaires

Nous avons mis en ligne le blog sur le serveur web et publié quelques articles très intéressants en utilisant Adminer. Les gens lisent notre blog et en sont très enthousiastes. Nous recevons chaque jour de nombreux e-mails de compliments. Mais à quoi servent tous ces éloges si nous ne les avons que dans nos e-mails et que personne ne peut les lire ? Il serait préférable que le lecteur puisse commenter directement l'article, afin que tout le monde puisse lire à quel point nous sommes géniaux.

Programmons donc les commentaires.

Création d'une nouvelle table

Lançons Adminer et créons une table comments avec les colonnes suivantes :

  • id int, cochez autoincrement (AI)
  • post_id, clé étrangère qui référence la table posts
  • name varchar, longueur 255
  • email varchar, longueur 255
  • content text
  • created_at timestamp

La table devrait donc ressembler à quelque chose comme ça :

N'oubliez pas d'utiliser à nouveau le stockage 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;

Formulaire de commentaire

Tout d'abord, nous devons créer un formulaire qui permettra aux utilisateurs de commenter les articles. Le Nette Framework a un support incroyable pour les formulaires. Nous pouvons les configurer dans le presenter et les afficher dans le template.

Le Nette Framework utilise le concept de composants. Un composant est une classe réutilisable, ou une partie de code, qui peut être attachée à un autre composant. Même le presenter est un composant. Chaque composant est créé via une factory. Créons donc une factory pour le formulaire de commentaire dans le presenter PostPresenter.

protected function createComponentCommentForm(): Form
{
	$form = new Form; // signifie Nette\Application\UI\Form

	$form->addText('name', 'Nom :')
		->setRequired();

	$form->addEmail('email', 'E-mail :');

	$form->addTextArea('content', 'Commentaire :')
		->setRequired();

	$form->addSubmit('send', 'Publier le commentaire');

	return $form;
}

Expliquons cela un peu plus. La première ligne crée une nouvelle instance du composant Form. Les méthodes suivantes attachent des entrées HTML à la définition de ce formulaire. ->addText sera rendu comme <input type="text" name="name"> avec <label>Nom :</label>. Comme vous l'avez probablement deviné, ->addTextArea sera rendu comme <textarea> et ->addSubmit comme <input type="submit">. Il existe de nombreuses autres méthodes similaires, mais celles-ci suffisent pour ce formulaire. Vous pouvez en lire plus dans la documentation.

Si le formulaire est déjà défini dans le presenter, nous pouvons le rendre (l'afficher) dans le template. Pour ce faire, placez la balise {control} à la fin du template qui affiche un article spécifique, dans Post/show.latte. Comme le composant s'appelle commentForm (le nom est dérivé du nom de la méthode createComponentCommentForm), la balise ressemblera à ceci :

...
<h2>Ajouter un nouveau commentaire</h2>

{control commentForm}

Maintenant, si vous affichez la page de détail de l'article, vous verrez le nouveau formulaire de commentaire à la fin.

Sauvegarde dans la base de données

Avez-vous déjà essayé de remplir et de soumettre le formulaire ? Vous avez probablement remarqué que le formulaire ne fait rien. Nous devons attacher une méthode de rappel qui enregistrera les données soumises.

Sur la ligne avant return dans la factory du composant commentForm, placez la ligne suivante :

$form->onSuccess[] = $this->commentFormSucceeded(...);

L'écriture précédente signifie “après la soumission réussie du formulaire, appelez la méthode commentFormSucceeded du presenter actuel”. Cependant, cette méthode n'existe pas encore. Créons-la :

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

Nous plaçons cette méthode juste après la factory du formulaire commentForm.

Cette nouvelle méthode a un argument, $data, qui est un objet contenant les données soumises du formulaire. Nous obtenons l'ID de l'article à partir des paramètres du presenter. Ensuite, nous insérons les données dans la table comments de la base de données.

Il y a encore deux autres méthodes qui méritent d'être expliquées. La méthode redirect('this') redirige vers la page actuelle. Il est conseillé de le faire après chaque soumission de formulaire réussie. Lorsque vous redirigez après une soumission de formulaire, vous ne verrez pas le message Voulez-vous renvoyer les données du formulaire ? que les navigateurs affichent parfois. (En général, après avoir soumis un formulaire avec la méthode POST, il devrait toujours y avoir une redirection vers une action GET.)

La méthode flashMessage() sert à afficher un message à l'utilisateur sur le résultat d'une opération. Comme nous redirigeons, le message ne peut pas être simplement transmis au template et rendu immédiatement. C'est pourquoi cette méthode existe : elle enregistre le message pour qu'il soit affiché lors du prochain chargement de la page. Les messages flash sont rendus dans le template principal app/Presentation/@layout.latte et ressemblent à ceci :

<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 transmis au template, nous n'avons donc pas besoin d'y penser beaucoup, cela fonctionne tout simplement. Pour plus d'informations, visitez la documentation.

Affichage des commentaires

C'est l'une de ces choses que vous allez adorer. Nette Database a une fonctionnalité géniale appelée Explorer. Vous souvenez-vous que nous avons délibérément créé les tables de la base de données en utilisant le stockage InnoDB ? Adminer a ainsi créé quelque chose appelé clés étrangères, qui nous épargnera beaucoup de travail.

Nette Database Explorer utilise les clés étrangères pour résoudre les relations entre les tables et, grâce à la connaissance de ces relations, peut créer automatiquement des requêtes de base de données.

Comme vous vous en souvenez sûrement, nous avons transmis la variable $post au template en utilisant la méthode PostPresenter::renderShow() et nous voulons maintenant itérer sur tous les commentaires dont la valeur de la colonne post_id est identique à $post->id. Nous pouvons y parvenir en appelant $post->related('comments'). Oui, c'est aussi simple que ça. Jetons un coup d'œil au code résultant :

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 écrit:</p>

		<div>{$comment->content}</div>
	{/foreach}
</div>
...

Notez l'attribut spécial n:tag-if. Vous savez déjà comment fonctionnent les n:attributs. Si vous ajoutez le préfixe tag- à l'attribut, la fonctionnalité ne s'applique qu'à la balise HTML, pas à son contenu. Cela nous permet de faire du nom du commentateur un lien uniquement s'il a fourni son e-mail. Ces deux lignes sont identiques :

<strong n:tag-if="$important"> Bonjour ! </strong>

{if $important}<strong>{/if} Bonjour ! {if $important}</strong>{/if}