Ustvarjamo kontaktni obrazec

Pogledali si bomo, kako v Nette ustvariti kontaktni obrazec, vključno s pošiljanjem na e-pošto. Pa začnimo!

Najprej moramo ustvariti nov projekt. Kako to storiti, pojasnjuje stran Začenjamo. Nato pa lahko že začnemo z ustvarjanjem obrazca.

Najenostavneje je ustvariti obrazec neposredno v presenterju. Lahko uporabimo vnaprej pripravljen HomePresenter. Vanjo dodamo komponento contactForm, ki predstavlja obrazec. To storimo tako, da v kodo zapišemo tovarniško metodo createComponentContactForm(), ki bo komponento izdelala:

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

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Ime:')
			->setRequired('Vnesite ime');
		$form->addEmail('email', 'E-pošta:')
			->setRequired('Vnesite e-pošto');
		$form->addTextarea('message', 'Sporočilo:')
			->setRequired('Vnesite sporočilo');
		$form->addSubmit('send', 'Pošlji');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, stdClass $data): void
	{
		// pošiljanje e-pošte
	}
}

Kot vidite, smo ustvarili dve metodi. Prva metoda createComponentContactForm() ustvari nov obrazec. Ta ima polja za ime, e-pošto in sporočilo, ki jih dodajamo z metodami addText(), addEmail() in addTextArea(). Dodali smo tudi gumb za pošiljanje obrazca. Kaj pa, če uporabnik ne izpolni katerega od polj? V takem primeru bi mu morali sporočiti, da je to obvezno polje. To smo dosegli z metodo setRequired(). Na koncu smo dodali tudi dogodek onSuccess, ki se sproži, če je obrazec uspešno poslan. V našem primeru pokliče metodo contactFormSucceeded, ki poskrbi za obdelavo poslanega obrazca. To bomo v kodo dodali čez trenutek.

Komponento contactForm bomo pustili izrisati v predlogi Home/default.latte:

{block content}
<h1>Kontaktni obrazec</h1>
{control contactForm}

Za samo pošiljanje e-pošte bomo ustvarili nov razred, ki ga bomo poimenovali ContactFacade in ga postavili v datoteko 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') // vaša e-pošta
			->setFrom($email, $name)
			->setSubject('Sporočilo iz kontaktnega obrazca')
			->setBody($message);

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

Metoda sendMessage() ustvari in pošlje e-pošto. Za to uporablja t.i. mailer, ki si ga pusti posredovati kot odvisnost prek konstruktorja. Preberite več o pošiljanju e-pošte.

Zdaj se vrnemo nazaj k presenterju in dokončamo metodo contactFormSucceeded(). Ta pokliče metodo sendMessage() razreda ContactFacade in ji posreduje podatke iz obrazca. In kako pridobimo objekt ContactFacade? Pustimo si ga posredovati s konstruktorjem:

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('Sporočilo je bilo poslano');
		$this->redirect('this');
	}
}

Ko je e-pošta poslana, uporabniku prikažemo še t.i. flash sporočilo, ki potrjuje, da je bilo sporočilo poslano, nato pa preusmerimo na naslednjo stran, da obrazca ni mogoče ponovno poslati s pomočjo refresh v brskalniku.

Tako, in če vse deluje, bi morali biti sposobni poslati e-pošto iz vašega kontaktnega obrazca. Čestitam!

HTML predloga e-pošte

Zaenkrat se pošilja navadno besedilno e-sporočilo, ki vsebuje samo sporočilo, poslano z obrazcem. V e-pošti pa lahko uporabimo HTML in naredimo njen videz privlačnejši. Zanjo bomo ustvarili predlogo v Latte, ki jo bomo zapisali v app/Model/contactEmail.latte:

<html>
	<title>Sporočilo iz kontaktnega obrazca</title>

	<body>
		<p><strong>Ime:</strong> {$name}</p>
		<p><strong>E-pošta:</strong> {$email}</p>
		<p><strong>Sporočilo:</strong> {$message}</p>
	</body>
</html>

Ostane še prilagoditi ContactFacade, da bo uporabljal to predlogo. V konstruktorju bomo zahtevali razred LatteFactory, ki zna izdelati objekt Latte\Engine, torej izrisovalnik Latte predlog. S pomočjo metode renderToString() bomo predlogo izrisali v datoteko, prvi parameter je pot do predloge, drugi pa so spremenljivke.

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') // vaša e-pošta
			->setFrom($email, $name)
			->setHtmlBody($body);

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

Generirano HTML e-pošto nato posredujemo metodi setHtmlBody() namesto prvotni setBody(). Prav tako nam ni treba navajati zadeve e-pošte v setSubject(), ker si jo bo knjižnica vzela iz elementa <title> predloge.

Konfiguracija

V kodi razreda ContactFacade je še vedno trdo kodiran naš administratorski e-naslov admin@example.com. Bolje bi bilo, da ga premaknemo v konfiguracijsko datoteko. Kako to storiti?

Najprej prilagodimo razred ContactFacade in niz z e-pošto nadomestimo s spremenljivko, posredovano s konstruktorjem:

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

Drugi korak pa je navedba vrednosti te spremenljivke v konfiguraciji. V datoteko app/config/services.neon zapišemo:

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

In to je to. Če bi bilo elementov v odseku services veliko in bi imeli občutek, da se e-pošta med njimi izgublja, jo lahko naredimo za spremenljivko. Prilagodimo zapis na:

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

In v datoteki app/config/common.neon definiramo to spremenljivko:

parameters:
	adminEmail: admin@example.com

In končano!