Création d'un formulaire de contact

Nous allons voir comment créer un formulaire de contact dans Nette, y compris l'envoi par e-mail. Alors, allons-y !

Tout d'abord, nous devons créer un nouveau projet. La page Démarrage explique comment faire. Ensuite, nous pouvons commencer à créer le formulaire.

Le plus simple est de créer le formulaire directement dans le presenter. Nous pouvons utiliser le HomePresenter pré-préparé. Nous y ajouterons le composant contactForm représentant le formulaire. Pour ce faire, nous écrirons dans le code une méthode factory createComponentContactForm() qui fabriquera le composant :

use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Nom :')
			->setRequired('Veuillez entrer votre nom');
		$form->addEmail('email', 'E-mail :')
			->setRequired('Veuillez entrer votre e-mail');
		$form->addTextarea('message', 'Message :')
			->setRequired('Veuillez entrer votre message');
		$form->addSubmit('send', 'Envoyer');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// envoi de l'e-mail
	}
}

Comme vous pouvez le voir, nous avons créé deux méthodes. La première méthode createComponentContactForm() crée un nouveau formulaire. Il comporte des champs pour le nom, l'e-mail et le message, que nous ajoutons avec les méthodes addText(), addEmail() et addTextArea(). Nous avons également ajouté un bouton pour soumettre le formulaire. Mais que se passe-t-il si l'utilisateur ne remplit pas un champ ? Dans ce cas, nous devrions lui faire savoir que c'est un champ obligatoire. Nous y sommes parvenus avec la méthode setRequired(). Enfin, nous avons également ajouté un événement onSuccess, qui se déclenche si le formulaire est soumis avec succès. Dans notre cas, il appelle la méthode contactFormSucceeded, qui se chargera du traitement du formulaire soumis. Nous ajouterons cela au code dans un instant.

Nous laisserons le composant contactForm se rendre dans le template Home/default.latte :

{block content}
<h1>Formulaire de contact</h1>
{control contactForm}

Pour l'envoi de l'e-mail lui-même, nous créerons une nouvelle classe que nous appellerons ContactFacade et la placerons dans le fichier app/Model/ContactFacade.php :

<?php
declare(strict_types=1);

namespace App\Model;

use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$mail = new Message;
		$mail->addTo('admin@example.com') // votre e-mail
			->setFrom($email, $name)
			->setSubject('Message du formulaire de contact')
			->setBody($message);

		$this->mailer->send($mail);
	}
}

La méthode sendMessage() crée et envoie l'e-mail. Pour ce faire, elle utilise ce qu'on appelle un mailer, qu'elle reçoit comme dépendance via le constructeur. Apprenez-en davantage sur l'envoi d'e-mails.

Maintenant, revenons au presenter et complétons la méthode contactFormSucceeded(). Elle appellera la méthode sendMessage() de la classe ContactFacade et lui transmettra les données du formulaire. Et comment obtenir l'objet ContactFacade ? Nous le laisserons nous être transmis par le constructeur :

use App\Model\ContactFacade;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	public function __construct(
		private ContactFacade $facade,
	) {
	}

	protected function createComponentContactForm(): Form
	{
		// ...
	}

	public function contactFormSucceeded(stdClass $data): void
	{
		$this->facade->sendMessage($data->email, $data->name, $data->message);
		$this->flashMessage('Le message a été envoyé');
		$this->redirect('this');
	}
}

Après l'envoi de l'e-mail, nous afficherons également à l'utilisateur un message flash confirmant que le message a été envoyé, puis nous redirigerons vers la page actuelle afin qu'il ne soit pas possible de soumettre à nouveau le formulaire en utilisant refresh dans le navigateur.

Voilà, et si tout fonctionne, vous devriez pouvoir envoyer un e-mail depuis votre formulaire de contact. Félicitations !

Template HTML de l'e-mail

Pour l'instant, un e-mail en texte brut est envoyé, contenant uniquement le message soumis par le formulaire. Mais nous pouvons utiliser le HTML dans l'e-mail et rendre son apparence plus attrayante. Nous allons créer un template pour cela en Latte, que nous écrirons dans app/Model/contactEmail.latte :

<html>
	<title>Message du formulaire de contact</title>

	<body>
		<p><strong>Nom :</strong> {$name}</p>
		<p><strong>E-mail :</strong> {$email}</p>
		<p><strong>Message :</strong> {$message}</p>
	</body>
</html>

Il reste à modifier ContactFacade, pour qu'il utilise ce template. Dans le constructeur, nous demanderons la classe LatteFactory, qui sait fabriquer un objet Latte\Engine, c'est-à-dire le moteur de rendu de templates Latte. Avec la méthode renderToString(), nous rendrons le template dans un fichier, le premier paramètre est le chemin vers le template et le second sont les variables.

namespace App\Model;

use Nette\Bridges\ApplicationLatte\LatteFactory;
use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$latte = $this->latteFactory->create();
		$body = $latte->renderToString(__DIR__ . '/contactEmail.latte', [
			'email' => $email,
			'name' => $name,
			'message' => $message,
		]);

		$mail = new Message;
		$mail->addTo('admin@example.com') // votre e-mail
			->setFrom($email, $name)
			->setHtmlBody($body);

		$this->mailer->send($mail);
	}
}

Nous transmettrons ensuite l'e-mail HTML généré à la méthode setHtmlBody() au lieu de l'original setBody(). De même, nous n'avons pas besoin de spécifier l'objet de l'e-mail dans setSubject(), car la bibliothèque le prendra à partir de l'élément <title> du template.

Configuration

Dans le code de la classe ContactFacade, notre e-mail administrateur admin@example.com est toujours codé en dur. Il serait préférable de le déplacer dans le fichier de configuration. Comment faire ?

Tout d'abord, modifions la classe ContactFacade et remplaçons la chaîne avec l'e-mail par une variable transmise par le constructeur :

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
		private string $adminEmail,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		// ...
		$mail = new Message;
		$mail->addTo($this->adminEmail)
			->setFrom($email, $name)
			->setHtmlBody($body);
		// ...
	}
}

Et la deuxième étape consiste à indiquer la valeur de cette variable dans la configuration. Dans le fichier config/services.neon (ou app/config/services.neon dans les versions plus anciennes), nous écrirons :

services:
	- App\Model\ContactFacade(adminEmail: admin@example.com)

Et c'est tout. S'il y a beaucoup d'éléments dans la section services et que vous avez l'impression que l'e-mail se perd parmi eux, nous pouvons en faire une variable. Modifions l'écriture en :

services:
	- App\Model\ContactFacade(adminEmail: %adminEmail%)

Et dans le fichier app/config/common.neon, nous définirons cette variable :

parameters:
	adminEmail: admin@example.com

Et c'est terminé !