Créons un formulaire de contact

Voyons comment créer dans Nette un formulaire de contact, envoi des données saisies par e-mail compris. Allons-y !

Nous devons d'abord créer un nouveau projet. La page Premiers pas explique comment faire. Nous pouvons ensuite commencer à créer le formulaire.

L'approche la plus simple est de créer le formulaire directement dans le presenter. Nous pouvons utiliser le HomePresenter déjà existant. Nous y ajouterons un composant nommé contactForm qui représentera notre formulaire. Pour cela, nous ajoutons au code du presenter une méthode fabrique createComponentContactForm(), qui créera 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 saisir votre nom');
		$form->addEmail('email', 'E-mail :')
			->setRequired('Veuillez saisir votre e-mail');
		$form->addTextArea('message', 'Message :')
			->setRequired('Veuillez saisir un message');
		$form->addSubmit('send', 'Envoyer');
		$form->onSuccess[] = $this->contactFormSucceeded(...);
		return $form;
	}

	private function contactFormSucceeded(Form $form, $data): void
	{
		// envoi d'un e-mail
	}
}

Comme vous le voyez, nous avons créé deux méthodes. La première, createComponentContactForm(), crée une nouvelle instance du formulaire. Elle contient les champs pour le nom, l'e-mail et le message, ajoutés respectivement par les méthodes addText(), addEmail() et addTextArea(). Nous avons aussi ajouté un bouton d'envoi. Mais que se passe-t-il si l'utilisateur laisse un champ vide ? Dans ce cas, nous devrions lui signaler que le champ est obligatoire. Nous y sommes parvenus à l'aide de la méthode setRequired(). Enfin, nous avons accroché un gestionnaire d'événement à onSuccess, qui est déclenché après l'envoi réussi du formulaire. Dans notre cas, il appelle la méthode contactFormSucceeded, qui se chargera du traitement des données soumises. Nous l'implémenterons dans un instant.

Rendons le composant contactForm 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 nommée ContactFacade et la placerons dans le fichier app/Model/ContactFacade.php :

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. Elle utilise pour cela un service mailer, qu'elle reçoit comme dépendance par le constructeur. Pour en savoir plus, voir l'envoi d'e-mails.

Revenons maintenant au presenter et complétons la méthode contactFormSucceeded(). Elle appellera la méthode sendMessage() de la classe ContactFacade en lui passant les données soumises par le formulaire. Et comment obtenir l'objet ContactFacade ? Nous le demanderons par le constructeur, grâce à l'injection de dépendances :

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 affichons à l'utilisateur un message flash qui confirme l'envoi. Puis nous redirigeons pour éviter que le formulaire ne soit renvoyé par un rafraîchissement du navigateur.

Si tout est bien en place, vous devriez donc maintenant pouvoir envoyer un e-mail depuis votre formulaire de contact. Félicitations !

Template HTML de l'e-mail

Pour l'instant, c'est un e-mail en texte brut, contenant seulement le message envoyé par le formulaire, qui est expédié. Nous pouvons cependant utiliser du HTML dans l'e-mail pour rendre son apparence plus attrayante. Nous en créerons un template en Latte et l'enregistrerons sous 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'elle utilise ce template. Dans le constructeur, nous demanderons la classe LatteFactory, qui sait créer un objet Latte\Engine, le moteur de rendu des templates Latte. À l'aide de la méthode renderToString(), nous rendons le template dans une chaîne. Le premier paramètre est le chemin du fichier de template, le second un tableau de variables à lui passer.

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 passons ensuite le contenu HTML généré de l'e-mail à la méthode setHtmlBody() au lieu du setBody() d'origine. Nous n'avons pas non plus besoin d'indiquer le sujet de l'e-mail avec setSubject(), car la bibliothèque l'extrait automatiquement de l'élément <title> du template.

Configuration

Dans le code de la classe ContactFacade, notre e-mail d'administrateur admin@example.com est toujours écrit en dur. Il vaudrait mieux le déplacer dans le fichier de configuration. Comment faire ?

Modifions d'abord la classe ContactFacade en remplaçant la chaîne d'e-mail écrite en dur par une variable passée au 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);
		// ...
	}
}

La deuxième étape consiste à fournir la valeur de cette variable dans la configuration. Dans le fichier app/config/services.neon, ajoutez :

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

Et voilà. Si la section services contient beaucoup d'entrées et que vous avez l'impression que l'adresse e-mail s'y perd, nous pouvons en faire un paramètre. Modifiez l'entrée ainsi :

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

Et définissez ce paramètre dans le fichier app/config/common.neon :

parameters:
	adminEmail: admin@example.com

Et c'est terminé !