Когда класс модели формы будет готов, вы наконец сможете использовать форму в методе действия контроллера.
Как вы уже, возможно, знаете, то, как пользователь работает с формой - это, как правило, итеративный процесс (схематически изображен на рисунке 7.18):
Базовый класс Form
предоставляет для этих целей несколько методов (см. таблицу 7.11).
Имя метода | Описание |
---|---|
setData($data) |
Задает данные формы для валидации. |
getData($flag) |
Извлекает валидированные данные. |
isValid() |
Валидирует форму. |
hasValidated() |
Проверяет, была ли форма валидирована. |
getMessages($elementName = null) |
Возвращает список сообщений о неудачных валидациях, если такие есть, для одного элемента или для всех элементов формы. |
Таким образом, общий процесс использования формы таков:
Проверяем, были ли отправлены данные формы, и, если нет, отображаем форму на веб-странице.
Если данные были отправлены пользователем, необработанные данные извлекаются из
переменных POST
и/или GET
в виде массива.
Данные присваиваются полям модели формы с использованием метода setData()
.
Выполняются фильтрация и валидация с использованием метода isValid()
(это приводит к выполнению фильтра входных данных, присоединенного к форме).
Если определенное поле (или поля) недействительно(ы), форма отображается снова,
и пользователю предлагается исправить данные.
Как только данные прошли фильтрацию и валидацию, они извлекаются из модели
формы методом getData()
и передаются другим моделям либо используются любым другим способом.
Пример кода ниже показывает, как реализовать этот процесс в методе действия контроллера:
<?php
namespace Application\Controller;
use Application\Form\ContactForm;
// ...
class IndexController extends AbstractActionController
{
// Это действие отображает форму обратной связи
public function contactUsAction()
{
// Создаем форму
$form = new ContactForm();
// Проверяем, отправил ли пользователь форму
if($this->getRequest()->isPost())
{
// Заполняем форму POST-данными
$data = $this->params()->fromPost();
$form->setData($data);
// Валидируем форму
if($form->isValid()) {
// Получаем фильтрованные и валидированные данные
$data = $form->getData();
// ... Какие-то действия с валидированными данными ...
// Перенаправление на страницу "Спасибо"
return $this->redirect()->toRoute('application', ['action'=>'thankYou']);
}
}
// Передаем переменную формы представлению
return new ViewModel([
'form' => $form
]);
}
}
В этом фрагменте мы определяем метод действия contactUsAction()
в классе IndexController
(строка 10).
В методе действия мы создаем экземпляр класса ContactForm
(строка 13).
Затем в строке 16 мы проверяем, является ли запрос POST-запросом (проверяя первую строку HTTP-запроса).
В строке 19 мы извлекаем необработанные данные, отправленные пользователем. Все переменные POST
извлекаются с помощью плагина контроллера Params
. Данные возвращаются в форме массива и сохраняются
в переменную $data
.
Данные, отправленные пользователем, могут содержать ошибки и должны пройти фильтрацию и валидацию перед
дальнейшим использованием. Для этого в строке 20 мы задаем эти данные модели с помощью метода setData()
базового
класса Form
. Мы затем валидируем данные формы методом isValid()
(строка 23), который возвращает значение true
при успешной валидации. Если валидация проходит успешно, мы извлекаем валидированные данные с помощью метода getData()
(строка 26) и затем можем передать их уровню бизнес-логики.
После использования валидированных данных мы перенаправляем веб-пользователю на страницу Thank You (строка 31).
Перенаправление осуществляется с помощью плагина контроллера Redirect
. Метод этого плагина toRoute()
принимает
два параметра: первый параметр - имя маршрута ("application"), а второй - массив параметров, которые нужно передать
маршрутизатору. Эти параметры определяют страницу, куда перенаправляется пользователь.
Мы подготовим действие контроллера и шаблон представления для страницы благодарности немного позже.
В строке 37 мы передаем модель формы шаблону представлению через переменную $form
. Шаблон представления
будет обращаться к этой переменной и использовать ее для визуализации формы (и возможных ошибок валидации).
В качестве реального примера использования валидированных данных формы обратной связи, в этом
примере мы создадим простой класс модели MailSender
?, которая может быть использована
для отправки электронного сообщения на адрес электронной почты. Когда пользователь отправит форму,
мы проведем валидацию данных формы, а затем передадим валидированные данные модели MailSender
и
потребуем отправить сообщение электронной почты получателю.
24) С точки зрения DDD, MailSender
можно отнести к моделям сервисов, так как ее задачей
является управление данными, а не их хранение.
Этот раздел по большей части предназначен для начинающих. Вы можете пропустить его и сразу перейти к следующему разделу Представление формы.
Модель MailSender
будет внутренне использовать компонент Zend\Mail
. Этот компонент, предоставляемый
Zend Framework 3, предназначен для обеспечения разработчика удобной функциональностью для составления
почтовых сообщений (класс Zend\Mail\Message
) и несколькими классами, реализующими доступные методы
передачи (в этом примере мы будем использовать класс Zend\Mail\Transport\Sendmail
, который использует программу
sendmail для доставки электронной почты).
Установите компонент
Zend\Mail
с помощью Composer'a, прописав следующую команду:
php composer.phar require zendframework/zend-mail
Программа sendmail - распространяемый бесплатно вместе с исходными кодами агент передачи почты для операционных систем Linux/Unix. Он принимает сообщения, передаваемые ему PHP-скриптом, решает, какой метод доставки использовать, в зависимости от заголовка сообщения, и затем передает сообщение через протокол SMTP на почтовый сервер (например, Google Mail) для доставки получателю.
Начнем с создания файла MailSender.php под каталогом Service под корневым каталогом модуля (см. рисунок 7.19).
Ниже представлен код, который нужно поместить в файл MailSender.php:
<?php
namespace Application\Service;
use Zend\Mail;
use Zend\Mail\Message;
use Zend\Mail\Transport\Sendmail;
// Этот класс используется для доставки электронного письма получателю.
class MailSender
{
// Отправляет эл. сообщение.
public function sendMail($sender, $recipient, $subject, $text)
{
$result = false;
try {
// Создаем эл. сообщение
$mail = new Message();
$mail->setFrom($sender);
$mail->addTo($recipient);
$mail->setSubject($subject);
$mail->setBody($text);
// Посылаем эл. сообщение
$transport = new Sendmail('-f'.$sender);
$transport->send($mail);
$result = true;
} catch(\Exception $e) {
$result = false;
}
// Возвращаем статус
return $result;
}
}
В этом фрагменте мы определяем пространство имен Application\Service
(строка 12), так как класс
MailSender
можно отнести к моделям сервисов (его задачей является управление данными, а не их хранение).
В строках 4-6 мы объявляем псевдонимы для классов Mail
, Message
и Zend\Mail\Transport\Sendmail
, предоставляемых
компонентом Zend\Mail
.
В строках 9-35 мы определяем класс MailSender
. У него есть один единственный метод sendMail()
(строка 12),
который принимает четыре аргумента: адрес электронной почты отправителя, адрес электронной почты получателя,
тема сообщения и, наконец, основной текст сообщения.
В строке 18 мы создаем экземпляр класса Message
. Мы используем методы этого класса, чтобы составить
сообщение (задать его тему, текст и т.д.) в строках 19-22.
В строке 25 мы создаем экземпляр класса Sendmail
, который использует программу sendmail для передачи
сообщения почтовому серверу (см. строки 25-26). Так как классы, предоставляемые компонентом Zend\Mail
могут
выбрасывать исключение при ошибке, мы заключим этот фрагмент кода в блок обработки исключений try-catch
Метод sendMail()
вернет значение true
, если сообщение было успешно отправлено; в противном случае он вернет
false
(строка 33).
Настройка почтовой системы для веб-сервера - это довольно сложная задача. Она, как правило, требует установки sendmail и настройки записи MX DNS сервера для использования определенного почтового сервера (либо локального, например, Posftix, либо удаленного вроде Google Mail). Из-за сложности этой темы, мы не обсуждаем ее в данной книге. Вы можете найти дополнительную информацию о настройки почты для вашей конкретной системы в Интернете.
Теперь зарегистрируем сервис MailSender
в файле module.config.php
следующим образом:
return [
//...
'service_manager' => [
'factories' => [
Service\MailSender::class => InvokableFactory::class,
],
],
//...
];
Далее вы сможете инстанцировать модель MailSender
в методе IndexController::contactUsAction()
и передать ей
валидированные данные формы.
Так как мы используем сервис
MailSender
в нашем контроллере, этот сервис является зависимостью для контроллера. Таким образом, нам необходимо будет создать фабрику для контроллера и внедрить зависимость в конструктор контроллера. Кажется сложным на первый взгляд, но когда вы улучшите свои навыки, то поймете, что это довольно просто и значительно улучшает структуру вашего кода.
Давайте создадим фабрику для IndexController
(поместим ее в подкаталог Factory
под каталогом Controller
).
Как видите, вся работа класса фабрики заключается в создании контроллера и передаче ему зависимости.
<?php
namespace Application\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\MailSender;
use Application\Controller\IndexController;
class IndexControllerFactory
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
$mailSender = $container->get(MailSender::class);
// Instantiate the controller and inject dependencies
return new IndexController($mailSender);
}
}
Изменим файл module.config.php
, чтобы он использовал только что созданную нами фабрику:
return [
//...
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\Factory\IndexControllerFactory::class,
],
],
//...
];
Затем добавим к контроллеру конструктор, а также методы contactUsAction()
, thankYouAction()
иsendErrorAction()
.
<?php
// ...
use Application\Service\MailSender;
class IndexController extends AbstractActionController
{
private $mailSender;
public function __construct($mailSender)
{
$this->mailSender = $mailSender;
}
public function contactUsAction()
{
// Создаем форму обратной связи
$form = new ContactForm();
// Проверяем, отправил ли пользователь форму
if($this->getRequest()->isPost()) {
// Заполняем форму POST-данными
$data = $this->params()->fromPost();
$form->setData($data);
// Валидируем форму
if($form->isValid()) {
// Получаем фильтрованные и валидированные данные
$data = $form->getData();
$email = $data['email'];
$subject = $data['subject'];
$body = $data['body'];
// Отправляем эл. почту
if(!$this->mailSender->sendMail('no-reply@example.com', $email,
$subject, $body)) {
// В случае ошибки перенаправляем на страницу "Ошибка отправки электронной почты"
return $this->redirect()->toRoute('application',
['action'=>'sendError']);
}
// Перенаправляем на страницу "Спасибо"
return $this->redirect()->toRoute('application',
['action'=>'thankYou']);
}
}
// Передаем переменную формы представлению
return new ViewModel([
'form' => $form
]);
}
// Это действие отображает страницу благодарности. Пользователь перенаправляется на эту
// страницу при успешной отправке письма.
public function thankYouAction()
{
return new ViewModel();
}
// Это действие отображает страницу ошибки. Пользователь перенаправляется на эту
// страницу в случае ошибки при отправке письма.
public function sendErrorAction()
{
return new ViewModel();
}
}
Как вы видите из фрагмента кода выше, мы делаем следующее:
В строке 3 мы определяем псевдоним для класса Application\Service\MailSender
.
Это позволит ссылаться на класс модели по его короткому имени.
В строках 32-34, после валидации формы, мы извлекаем валидированные значениями
полей в PHP-переменные $email
, $subject
и $body
.
В строке 37 мы вызываем метод sendMail()
сервиса MailSender
и передаем ему
четыре параметра: адрес отправителя (в примере мы используем "no-reply@example.com", но
вы можете заменить его своим); адрес электронной почты получателя, тему электронного
сообщение и его основной текст.
Если письмо было успешно отправлено (если метод sendMail() вернул значение
true), мы
перенаправляем пользователя на страницу *Thank You* (строка 45). В случае ошибки (если
метод
sendMail() вернул значение false
) пользователь перенаправляется на страницу
Send Error (строка 40).
В строках 58-61 находится метод thankYouAction()
, отображающий страницу благодарности.
Эта страница показывается при успешной отправке письма.
В строках 65-68 находится метод sendErrorAction()
, отображающий страницу ошибки отправки письма.
Эта страница показывается при неудачной отправке.