Stwórzmy formularz kontaktowy
Przyjrzyjmy się jak stworzyć formularz kontaktowy w Nette, łącznie z wysłaniem go na maila. A więc do dzieła!
Najpierw musimy stworzyć nowy projekt. Jak wyjaśnia strona Getting Started. A następnie możemy przystąpić do tworzenia formularza.
Najprostszym sposobem jest stworzenie formularza bezpośrednio w
Presenterze. Możemy skorzystać z gotowego HomePresenter
. Dodamy komponent contactForm
reprezentujący formularz. Zrobimy to wpisując metodę createComponentContactForm()
factory do kodu, który będzie
wytwarzał komponent:
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
}
}
Jak widać, stworzyliśmy dwie metody. Pierwsza metoda createComponentContactForm()
tworzy nowy formularz. Ma on
pola na imię, e-mail i wiadomość, które dodajemy za pomocą metod addText()
, addEmail()
i
addTextArea()
. Dodaliśmy również przycisk do wysłania formularza. Ale co jeśli użytkownik nie wypełni
niektórych pól? W takim przypadku powinniśmy dać mu znać, że jest to pole wymagane. Zrobiliśmy to za pomocą metody
setRequired()
. Na koniec dodaliśmy również zdarzenie
onSuccess
, które jest wywoływane, jeśli formularz zostanie przesłany pomyślnie. W naszym przypadku wywołuje ono
metodę contactFormSucceeded
, która zajmuje się przetwarzaniem przesłanego formularza. Za chwilę dodamy to
do kodu.
Niech komponent contantForm
będzie renderowany w szablonie Home/default.latte
:
{block content}
<h1>Contant Form</h1>
{control contactForm}
Aby wysłać sam e-mail, tworzymy nową klasę o nazwie ContactFacade
i umieszczamy ją w pliku
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()
będzie tworzyć i wysyłać e-mail. Używa do tego tzw. mailera, którego przekazuje jako
zależność poprzez konstruktor. Przeczytaj więcej o wysyłaniu e-maili.
Teraz wrócimy do prezentera i zakończymy pracę nad metodą contactFormSucceeded()
. Wywołuje ona metodę
sendMessage()
klasy ContactFacade
i przekazuje jej dane formularza. A jak otrzymamy obiekt
ContactFacade
? Będziemy mieli go przekazanego nam przez konstruktor:
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');
}
}
Po wysłaniu maila pokazujemy użytkownikowi tzw. flash message, potwierdzający wysłanie wiadomości, a następnie przekierowujemy na następną stronę, aby nie można było ponownie wysłać formularza za pomocą refresh w przeglądarce.
No cóż, jeśli wszystko działa, powinieneś móc wysłać maila ze swojego formularza kontaktowego. Gratulacje!!!
Szablon HTML Email
Na razie wysyłany jest zwykły email tekstowy zawierający tylko wiadomość wysłaną przez formularz. Możemy jednak
wykorzystać w mailu HTML i uatrakcyjnić go. Stworzymy do tego szablon w Latte, który zapiszemy w
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>
Pozostaje jeszcze zmodyfikować ContactFacade
, aby wykorzystać ten szablon. W konstruktorze żądamy klasy
LatteFactory
, która może wyprodukować obiekt Latte\Engine
, czyli renderer szablonu Latte. Używamy metody
renderToString()
do renderowania szablonu do pliku, pierwszy parametr to ścieżka do szablonu, a drugi to
zmienne.
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);
}
}
Następnie przekazujemy wygenerowany e-mail HTML do metody setHtmlBody()
zamiast oryginalnego
setBody()
. Nie musimy również określać tematu wiadomości w setSubject()
, ponieważ biblioteka
pobiera go z elementu <title>
w szablonie.
Konfiguracja
W kodzie klasy ContactFacade
nasz adminowy email admin@example.com
jest wciąż hardcoded. Lepiej
byłoby przenieść go do pliku konfiguracyjnego. Jak to zrobić?
Najpierw modyfikujemy klasę ContactFacade
i zamieniamy ciąg email na zmienną przekazywaną przez
konstruktor:
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);
// ...
}
}
A drugi krok to umieszczenie wartości tej zmiennej w konfiguracji. W pliku app/config/services.neon
dodajemy:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
I to wszystko. Jeśli w sekcji services
jest dużo pozycji i mamy wrażenie, że mail się wśród nich gubi,
możemy uczynić go zmienną. Zmodyfikujemy wpis na:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
I zdefiniujemy tę zmienną w pliku app/config/common.neon
:
parameters:
adminEmail: admin@example.com
I gotowe!