Creăm un formular de contact

Vom analiza cum să creăm un formular de contact în Nette, inclusiv trimiterea pe email. Să începem!

Mai întâi trebuie să creăm un proiect nou. Cum se face acest lucru este explicat pe pagina Începeți. Apoi putem începe crearea formularului.

Cel mai simplu este să creăm formularul direct în presenter. Putem folosi HomePresenter pre-pregătit. În el vom adăuga componenta contactForm care reprezintă formularul. Vom face acest lucru scriind în cod metoda fabrică createComponentContactForm(), care va produce componenta:

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

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Nume:')
			->setRequired('Introduceți numele');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Introduceți e-mailul');
		$form->addTextarea('message', 'Mesaj:')
			->setRequired('Introduceți mesajul');
		$form->addSubmit('send', 'Trimite');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// trimiterea emailului
	}
}

După cum vedeți, am creat două metode. Prima metodă createComponentContactForm() creează un nou formular. Acesta are câmpuri pentru nume, email și mesaj, pe care le adăugăm cu metodele addText(), addEmail() și addTextArea(). Am adăugat și un buton pentru trimiterea formularului. Dar ce se întâmplă dacă utilizatorul nu completează un câmp? În acest caz, ar trebui să-l informăm că este un câmp obligatoriu. Am realizat acest lucru cu metoda setRequired(). În final, am adăugat și evenimentul onSuccess, care se declanșează dacă formularul este trimis cu succes. În cazul nostru, apelează metoda contactFormSucceeded, care se ocupă de procesarea formularului trimis. Vom completa codul pentru aceasta imediat.

Vom lăsa componenta contactForm să fie redată în șablonul Home/default.latte:

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

Pentru trimiterea efectivă a emailului, vom crea o nouă clasă, pe care o vom numi ContactFacade și o vom plasa în fișierul 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') // emailul dvs.
			->setFrom($email, $name)
			->setSubject('Mesaj din formularul de contact')
			->setBody($message);

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

Metoda sendMessage() creează și trimite emailul. Utilizează pentru aceasta așa-numitul mailer, pe care îl primește ca dependență prin constructor. Citiți mai multe despre trimiterea emailurilor.

Acum ne vom întoarce la presenter și vom finaliza metoda contactFormSucceeded(). Aceasta va apela metoda sendMessage() a clasei ContactFacade și îi va transmite datele din formular. Și cum obținem obiectul ContactFacade? Îl vom primi prin constructor:

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('Mesajul a fost trimis');
		$this->redirect('this');
	}
}

După ce emailul este trimis, vom afișa utilizatorului un așa-numit flash message, confirmând că mesajul a fost trimis, și apoi vom redirecționa către aceeași pagină (pentru a curăța formularul), astfel încât să nu fie posibilă retrimiterea formularului prin refresh în browser.

Deci, dacă totul funcționează, ar trebui să puteți trimite un email din formularul dvs. de contact. Felicitări!

Șablon HTML pentru email

Deocamdată se trimite un email text simplu care conține doar mesajul trimis prin formular. Dar în email putem folosi HTML și să-i facem aspectul mai atractiv. Vom crea un șablon pentru el în Latte, pe care îl vom scrie în app/Model/contactEmail.latte:

<html>
	<title>Mesaj din formularul de contact</title>

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

Rămâne să modificăm ContactFacade pentru a utiliza acest șablon. În constructor vom solicita clasa LatteFactory, care poate produce obiectul Latte\Engine, adică motorul de redare a șabloanelor Latte. Folosind metoda renderToString(), vom reda șablonul într-un șir, primul parametru este calea către șablon și al doilea sunt variabilele.

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

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

Emailul HTML generat îl vom transmite apoi metodei setHtmlBody() în locul celei originale setBody(). De asemenea, nu trebuie să specificăm subiectul emailului în setSubject(), deoarece biblioteca îl va prelua din elementul <title> al șablonului.

Configurare

În codul clasei ContactFacade este încă hardcodat emailul nostru de administrator admin@example.com. Ar fi mai bine să-l mutăm în fișierul de configurare. Cum facem asta?

Mai întâi modificăm clasa ContactFacade și înlocuim șirul cu emailul cu o variabilă transmisă prin constructor:

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

Și al doilea pas este specificarea valorii acestei variabile în configurație. În fișierul app/config/services.neon scriem:

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

Și gata. Dacă ar fi multe elemente în secțiunea services și ați avea senzația că emailul se pierde printre ele, îl putem transforma într-o variabilă. Modificăm înregistrarea la:

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

Și în fișierul app/config/common.neon definim această variabilă:

parameters:
	adminEmail: admin@example.com

Și am terminat!