Створюємо контактну форму
Розглянемо, як у Nette створити контактну форму, включно з надсиланням на електронну пошту. Отже, до справи!
Спочатку потрібно створити новий проект. Як це зробити, пояснюється на сторінці Починаємо. А потім вже можемо почати створювати форму.
Найпростіше створити форму
безпосередньо в презентері. Можемо використати заготовлений
HomePresenter
. До нього додамо компонент contactForm
, що представляє
форму. Зробимо це так: запишемо в код фабричний метод
createComponentContactForm()
, який створить компонент:
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;
class HomePresenter extends Presenter
{
protected function createComponentContactForm(): Form
{
$form = new Form;
$form->addText('name', "Ім'я:")
->setRequired("Введіть ім'я");
$form->addEmail('email', 'E-mail:')
->setRequired('Введіть e-mail');
$form->addTextarea('message', 'Повідомлення:')
->setRequired('Введіть повідомлення');
$form->addSubmit('send', 'Надіслати');
$form->onSuccess[] = [$this, 'contactFormSucceeded'];
return $form;
}
public function contactFormSucceeded(Form $form, $data): void
{
// надсилання email
}
}
Як бачите, ми створили два методи. Перший метод createComponentContactForm()
створює нову форму. Вона має поля для імені, email та повідомлення, які ми
додаємо методами addText()
, addEmail()
та addTextArea()
. Також ми
додали кнопку для надсилання форми. Але що, якщо користувач не
заповнить якесь поле? У такому випадку ми повинні повідомити йому, що
це обов'язкове поле. Цього ми досягли за допомогою методу
setRequired()
. Нарешті, ми також додали подію onSuccess
, яка спрацює, якщо
форма успішно надіслана. У нашому випадку вона викличе метод
contactFormSucceeded
, який подбає про обробку надісланої форми. Це ми
доповнимо в код за мить.
Компонент contactForm
виведемо в шаблоні Home/default.latte
:
{block content}
<h1>Контактна форма</h1>
{control contactForm}
Для самого надсилання email створимо новий клас, який назвемо
ContactFacade
і розмістимо його у файлі 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') // ваш email
->setFrom($email, $name)
->setSubject('Повідомлення з контактної форми')
->setBody($message);
$this->mailer->send($mail);
}
}
Метод sendMessage()
створює та надсилає email. Для цього він
використовує так званий mailer, який отримує як залежність через
конструктор. Дізнайтеся більше про надсилання
електронних листів.
Тепер повернемося до презентера і завершимо метод
contactFormSucceeded()
. Він викличе метод sendMessage()
класу
ContactFacade
і передасть йому дані з форми. А як отримати об'єкт
ContactFacade
? Отримаємо його через конструктор:
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('Повідомлення було надіслано');
$this->redirect('this');
}
}
Після надсилання email ми ще покажемо користувачеві так зване flash-повідомлення, що підтверджує надсилання повідомлення, а потім перенаправимо на наступну сторінку, щоб не можна було повторно надіслати форму за допомогою refresh у браузері.
Отже, якщо все працює, ви повинні мати можливість надіслати email з вашої контактної форми. Вітаю!
HTML-шаблон електронного листа
Поки що надсилається простий текстовий email, що містить лише
повідомлення, надіслане формою. Але в email ми можемо використовувати HTML
і зробити його вигляд привабливішим. Створимо для нього шаблон у Latte,
який запишемо до app/Model/contactEmail.latte
:
<html>
<title>Повідомлення з контактної форми</title>
<body>
<p><strong>Ім'я:</strong> {$name}</p>
<p><strong>E-mail:</strong> {$email}</p>
<p><strong>Повідомлення:</strong> {$message}</p>
</body>
</html>
Залишилося змінити ContactFacade
, щоб він використовував цей шаблон.
У конструкторі ми запросимо клас LatteFactory
, який вміє створювати
об'єкт Latte\Engine
, тобто рендер
шаблонів Latte. За допомогою методу renderToString()
ми відрендеримо
шаблон у файл, першим параметром є шлях до шаблону, а другим –
змінні.
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') // ваш email
->setFrom($email, $name)
->setHtmlBody($body);
$this->mailer->send($mail);
}
}
Згенерований HTML email потім передамо методу setHtmlBody()
замість
початкового setBody()
. Також нам не потрібно вказувати тему email у
setSubject()
, оскільки бібліотека візьме її з елемента <title>
шаблону.
Конфігурація
У коді класу ContactFacade
все ще жорстко прописаний наш
адміністраторський email admin@example.com
. Було б краще перенести його до
конфігураційного файлу. Як це зробити?
Спочатку змінимо клас ContactFacade
і рядок з email замінимо змінною,
переданою конструктором:
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);
// ...
}
}
А другим кроком є вказівка значення цієї змінної в конфігурації. До
файлу app/config/services.neon
запишемо:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
І все. Якщо елементів у секції services
буде багато і ви
відчуватимете, що email серед них губиться, ми можемо зробити з нього
змінну. Змінимо запис на:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
А у файлі app/config/common.neon
визначимо цю змінну:
parameters:
adminEmail: admin@example.com
І готово!