Создадим форму обратной связи
Разберём, как создать в 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;
}
private function contactFormSucceeded(Form $form, $data): void
{
// отправка письма
}
}
Как видите, мы создали два метода. Первый, createComponentContactForm(),
создаёт новый экземпляр формы. В нём есть поля для имени, e-mail и
сообщения, добавленные соответственно методами addText(),
addEmail() и addTextArea(). Мы также добавили кнопку отправки. А что,
если пользователь оставит поле пустым? В этом случае нам следует
сообщить ему, что поле обязательно. Этого мы добились методом
setRequired(). Наконец, мы привязали обработчик события к onSuccess, который
срабатывает при успешной отправке формы. В нашем случае он вызывает
метод contactFormSucceeded, который займётся обработкой отправленных
данных. Реализуем его чуть позже.
Отрисуем компонент contactForm в шаблоне Home/default.latte:
{block content}
<h1>Форма обратной связи</h1>
{control contactForm}
Для самой отправки письма создадим новый класс с именем
ContactFacade и поместим его в файл app/Model/ContactFacade.php:
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') // ваш e-mail
->setFrom($email, $name)
->setSubject('Сообщение из формы обратной связи')
->setBody($message);
$this->mailer->send($mail);
}
}
Метод sendMessage() создаёт и отправляет письмо. Для этого он
использует сервис отправки почты, который получает как зависимость
через конструктор. Подробнее об отправке
писем.
Теперь вернёмся в презентер и допишем метод 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');
}
}
После отправки письма мы показываем пользователю flash-сообщение, подтверждающее отправку. Затем перенаправляем, чтобы форму нельзя было отправить повторно обновлением страницы в браузере.
Итак, если всё настроено правильно, теперь вы должны иметь возможность отправить письмо из своей формы обратной связи. Поздравляем!
HTML-шаблон письма
Сейчас отправляется обычное текстовое письмо, содержащее только
сообщение, отправленное через форму. Однако мы можем использовать в
письме 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') // ваш e-mail
->setFrom($email, $name)
->setHtmlBody($body);
$this->mailer->send($mail);
}
}
Получившееся HTML-содержимое письма мы затем передаём в метод
setHtmlBody() вместо исходного setBody(). Тему письма через
setSubject() указывать тоже не нужно, потому что библиотека
автоматически берёт её из элемента <title> в шаблоне.
Настройка
В коде класса ContactFacade наш e-mail администратора admin@example.com
по-прежнему прописан жёстко. Лучше было бы перенести его в
конфигурационный файл. Как это сделать?
Сначала изменим класс ContactFacade, заменив жёстко прописанную
строку с e-mail переменной, передаваемой через конструктор:
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 много записей и вам кажется, что
адрес электронной почты в них теряется, его можно превратить в
параметр. Измените запись так:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
А сам параметр определите в файле app/config/common.neon:
parameters:
adminEmail: admin@example.com
И готово!