Creiamo un form di contatto

Vediamo come creare in Nette un form di contatto, compreso l'invio per email dei dati inseriti. Cominciamo!

Per prima cosa dobbiamo creare un nuovo progetto. Come farlo lo spiega la pagina Per iniziare. Poi possiamo passare alla creazione del form.

Il modo più semplice è creare il form direttamente nel presenter. Possiamo sfruttare l'HomePresenter già pronto. Vi aggiungeremo il componente contactForm, che rappresenta il nostro form. Lo facciamo aggiungendo al codice del presenter il metodo factory createComponentContactForm(), che creerà il componente:

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

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Nome:')
			->setRequired('Inserite il vostro nome');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Inserite la vostra email');
		$form->addTextArea('message', 'Messaggio:')
			->setRequired('Inserite un messaggio');
		$form->addSubmit('send', 'Invia');
		$form->onSuccess[] = $this->contactFormSucceeded(...);
		return $form;
	}

	private function contactFormSucceeded(Form $form, $data): void
	{
		// invio dell'email
	}
}

Come vedete abbiamo creato due metodi. Il primo, createComponentContactForm(), crea una nuova istanza del form. Contiene i campi per il nome, l'email e il messaggio, aggiunti rispettivamente con i metodi addText(), addEmail() e addTextArea(). Abbiamo aggiunto anche il pulsante di invio. E se l'utente lasciasse un campo vuoto? In quel caso dovremmo avvertirlo che il campo è obbligatorio. L'abbiamo ottenuto con il metodo setRequired(). Infine abbiamo agganciato a onSuccess un gestore di evento, che si attiva quando il form viene inviato con successo. Nel nostro caso chiama il metodo contactFormSucceeded, che si occuperà di elaborare i dati inseriti. Lo scriveremo tra un attimo.

Rendiamo il componente contactForm nel template Home/default.latte:

{block content}
<h1>Form di contatto</h1>
{control contactForm}

Per l'invio vero e proprio dell'email creeremo una nuova classe chiamata ContactFacade e la metteremo nel file 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') // la vostra email
			->setFrom($email, $name)
			->setSubject('Messaggio dal form di contatto')
			->setBody($message);

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

Il metodo sendMessage() crea e invia l'email. Per farlo usa un cosiddetto mailer, che riceve come dipendenza tramite il costruttore. Leggete di più sull'invio delle email.

Torniamo ora al presenter e completiamo il metodo contactFormSucceeded(). Chiamerà il metodo sendMessage() della classe ContactFacade passandogli i dati inseriti nel form. E come otteniamo l'oggetto ContactFacade? Ce lo faremo passare tramite il costruttore usando la dependency injection:

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('Il messaggio è stato inviato');
		$this->redirect('this');
	}
}

Dopo l'invio dell'email mostriamo all'utente un messaggio flash che conferma l'avvenuto invio. Poi reindirizziamo, perché il form non possa essere reinviato aggiornando la pagina nel browser.

Se tutto è impostato correttamente, dovreste ora riuscire a inviare un'email dal vostro form di contatto. Congratulazioni!

Template HTML dell'email

Per ora viene inviata una email in testo semplice che contiene solo il messaggio inserito nel form. Nell'email possiamo però usare l'HTML e renderne l'aspetto più gradevole. Creeremo il template in Latte e lo salveremo come app/Model/contactEmail.latte:

<html>
	<title>Messaggio dal form di contatto</title>

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

Resta da modificare ContactFacade perché usi questo template. Nel costruttore ci faremo passare la classe LatteFactory, che sa creare l'oggetto Latte\Engine, cioè il renderer dei template Latte. Con il metodo renderToString() renderizziamo il template in una stringa. Il primo parametro è il percorso del file del template, il secondo un array di variabili da passargli.

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') // la vostra email
			->setFrom($email, $name)
			->setHtmlBody($body);

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

Il contenuto HTML dell'email generato lo passiamo poi al metodo setHtmlBody() invece dell'originale setBody(). Non dobbiamo nemmeno indicare l'oggetto dell'email con setSubject(), perché la libreria lo ricava automaticamente dall'elemento <title> del template.

Configurazione

Nel codice della classe ContactFacade è ancora scritta fissa la nostra email di amministrazione admin@example.com. Sarebbe meglio spostarla nel file di configurazione. Come si fa?

Per prima cosa modifichiamo la classe ContactFacade sostituendo la stringa con l'email con una variabile passata dal costruttore:

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);
		// ...
	}
}

Il secondo passo è indicare il valore di questa variabile nella configurazione. Nel file app/config/services.neon aggiungiamo:

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

E questo è tutto. Se nella sezione services ci fossero molte voci e vi sembrasse che l'indirizzo email vi si perde, possiamo trasformarlo in un parametro. Modifichiamo la voce così:

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

E nel file app/config/common.neon definiamo questo parametro:

parameters:
	adminEmail: admin@example.com

Ed è fatta!