Să creăm un formular de contact

Haideți să vedem cum se creează un formular de contact în Nette, inclusiv trimiterea acestuia la un e-mail. Deci, să o facem!

Mai întâi trebuie să creăm un nou proiect. După cum explică pagina de început. Și apoi putem începe să creăm formularul.

Cel mai simplu mod este să creăm formularul direct în Presenter. Putem folosi formularul pre-fabricat HomePresenter. Vom adăuga componenta contactForm care reprezintă formularul. Vom face acest lucru scriind metoda factory createComponentContactForm() în codul 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', 'Name:')
			->setRequired('Enter your name');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Enter your e-mail');
		$form->addTextarea('message', 'Message:')
			->setRequired('Enter message');
		$form->addSubmit('send', 'Send');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// sending an email
	}
}

După cum puteți vedea, am creat două metode. Prima metodă createComponentContactForm() creează un nou formular. Acesta are câmpuri pentru nume, e-mail și mesaj, pe care le adăugăm cu ajutorul metodelor addText(), addEmail() și addTextArea(). De asemenea, am adăugat un buton pentru a trimite formularul. Dar ce se întâmplă dacă utilizatorul nu completează unele câmpuri? În acest caz, ar trebui să-l anunțăm că este un câmp obligatoriu. Am făcut acest lucru cu metoda setRequired(). În cele din urmă, am adăugat și un eveniment onSuccess, care este declanșat dacă formularul este trimis cu succes. În cazul nostru, acesta apelează metoda contactFormSucceeded, care se ocupă de procesarea formularului trimis. Vom adăuga acest lucru în cod imediat.

Lăsați componenta contantForm să fie redată în șablonul templates/Home/default.latte:

{block content}
<h1>Contant Form</h1>
{control contactForm}

Pentru a trimite e-mailul propriu-zis, creăm o nouă clasă numită ContactFacade și o plasăm î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') // your email
			->setFrom($email, $name)
			->setSubject('Message from the contact form')
			->setBody($message);

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

Metoda sendMessage() va crea și va trimite e-mailul. Pentru a face acest lucru, folosește un așa-numit mailer, pe care îl transmite ca dependență prin intermediul constructorului. Citiți mai multe despre trimiterea de e-mailuri.

Acum, ne vom întoarce la prezentator și vom finaliza metoda contactFormSucceeded(). Acesta apelează metoda sendMessage() a clasei ContactFacade și îi transmite datele formularului. Și cum obținem obiectul ContactFacade? Ne va fi transmis de către 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('The message has been sent');
		$this->redirect('this');
	}
}

După ce e-mailul este trimis, îi arătăm utilizatorului așa-numitul mesaj flash, confirmând că mesajul a fost trimis, și apoi îl redirecționăm către pagina următoare, astfel încât formularul să nu poată fi trimis din nou folosind refresh în browser.

Ei bine, dacă totul funcționează, ar trebui să puteți trimite un e-mail din formularul de contact. Felicitări!

Șablon de e-mail HTML

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

<html>
	<title>Message from the contact form</title>

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

Rămâne să modificăm ContactFacade pentru a utiliza acest șablon. În constructor, solicităm clasa LatteFactory, care poate produce obiectul Latte\Engine, un renderizator de șabloane Latte. Utilizăm metoda renderToString() pentru a reda șablonul într-un fișier, primul parametru este calea către șablon, iar 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') // your email
			->setFrom($email, $name)
			->setHtmlBody($body);

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

Apoi, trecem e-mailul HTML generat la metoda setHtmlBody() în loc de originalul setBody(). De asemenea, nu trebuie să specificăm subiectul e-mailului în setSubject(), deoarece biblioteca îl preia din elementul <title> din șablon.

Configurarea

În codul clasei ContactFacade, e-mailul nostru de administrare admin@example.com este încă codificat în mod greșit. Ar fi mai bine să îl mutați în fișierul de configurare. Cum se face acest lucru?

Mai întâi, modificăm clasa ContactFacade și înlocuim șirul de e-mail cu o variabilă transmisă de 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);
		// ...
	}
}

Iar al doilea pas este să introducem valoarea acestei variabile în configurație. În fișierul app/config/services.neon adăugăm:

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

Și asta e tot. Dacă există multe elemente în secțiunea services și aveți impresia că e-mailul se pierde printre ele, îl putem transforma într-o variabilă. Vom modifica intrarea în:

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

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

parameters:
	adminEmail: admin@example.com

Și gata!