Создаем контактную форму
Посмотрим, как в Nette создать контактную форму, включая отправку на email. Итак, приступим!
Сначала нам нужно создать новый проект. Как это сделать, объясняется на странице Начало работы. А затем уже можно приступать к созданию формы.
Проще всего создать форму прямо в
презентере. Мы можем использовать готовый 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, который получает как зависимость через
конструктор. Узнайте больше об отправке email.
Теперь вернемся к презентеру и завершим метод 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, содержащий только
сообщение, отправленное формой. Но в 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
И готово!