Cuando la clase para el modelo de formulario esta lista podemos usar finalmente el formulario en un método de acción del controlador.
Como ya deberíamos saber la manera en que el sitio funciona junto al formulario es generalmente un proceso interactivo (ilustrado esquemáticamente en la figura 7.18).
Primero mostramos el formulario y sus campos en una página, un cursor indicará los campos que el usuario debe llenar. El usuario llena los campos del formulario y hace clic en el botón Submit que envía los datos al servidor.
Luego nuestro controlador extrae los datos enviados y pide su validación al modelo de formulario. Si existen errores de entrada se muestra de nuevo el formulario pidiendo al usuario que corrija los errores de entrada. Si los datos son correctos procesamos los datos con la capa de lógica de negocio y (generalmente) dirigimos al usuario a otra pagina web.
La clase base Form
provee varios métodos para alcanzar este objetivos
(ver tabla 7.11).
Nombre del método | Descripción |
---|---|
setData($data) |
Coloca los datos del formulario para la validación. |
getData($flag) |
Recupera los datos validados. |
isValid() |
Valida el formulario. |
hasValidated() |
Revisa si el formulario ha sido validado. |
getMessages($elementName = null) |
Regresa una lista de mensajes de fracaso en la validación, si se indica solo para un elemento o para todos los elementos del formulario. |
Así el flujo de uso de un formulario genérico es el siguiente:
Revisar si los datos del formulario se han enviado y si no mostrar el formulario en la página web.
Si los datos fueron enviados por el usuario del sitio se recuperan los datos
crudos desde la variable POST
(y/o GET
) en forma de un arreglo.
Los datos se asignan a los campos del modelo de formulario usando el método
setData()
.
Los filtros y validadores son ejecutados usando el método de formulario
isValid()
(esto da como resultado la ejecución de los filtros de entrada
asociados al formulario). Si determinados campos son inválidos se muestra el
formulario nuevamente y se pide al usuario que corrija los datos ingresados.
Tan pronto como los datos se han filtrado/validado recuperamos los datos del
formulario el modelo del formulario usa el método getData()
y podemos pasar
los datos a otros modelos o usarlos de cualquier otra manera.
El código de abajo muestra como implementar este flujo de trabajo típico en nuestro método de acción en el controlador:
<?php
namespace Application\Controller;
use Application\Form\ContactForm;
// ...
class IndexController extends AbstractActionController
{
// This action displays the feedback form
public function contactUsAction()
{
// Create Contact Us form
$form = new ContactForm();
// Check if user has submitted the form
if($this->getRequest()->isPost())
{
// Fill in the form with POST data
$data = $this->params()->fromPost();
$form->setData($data);
// Validate form
if($form->isValid()) {
// Get filtered and validated data
$data = $form->getData();
// ... Do something with the validated data ...
// Redirect to "Thank You" page
return $this->redirect()->toRoute('application', ['action'=>'thankYou']);
}
}
// Pass form variable to view
return new ViewModel([
'form' => $form
]);
}
}
En el código de arriba definimos el método de acción contactUsAction()
dentro
de la clase IndexController
(línea 10). En el método de acción creamos una
instancia de la clase ContactForm
(línea 13).
Luego en la línea 16 revisamos si la petición es una petición POST (revisando la primera línea de la petición HTTP).
En la línea 19 recuperamos los datos crudos enviados por el usuario. Extraemos
todas las variables POST con la ayuda del complemento para controladores Params
.
Los datos se regresan en forma de un arreglo y se guardan dentro de la variable
$data
.
Los datos enviados por el usuario pueden contener errores y deberían ser filtrados
y validados antes de su uso. Para hacer esto, en la línea 20 colocamos los datos
en el modelo de formulario con el método setData()
que provee la clase base
Form
. Validamos los datos del formulario con el método isValid()
(línea 23),
que regresa true
si la validación es exitosa. Si la validación es exitosa
recuperamos los datos validados usando el método getData()
(línea 26) y luego
podemos pasar los datos a nuestra capa de lógica de negocio.
Una vez que hemos usado los datos validados en la línea 31 dirigimos al
usuario de la página web a la página Gracias a ti. Para redirigir se ejecuta
el complemento para controladores Redirect
. El método toRoute()
del
complemento Redirect
toma dos parámetros: el primer parámetro es el nombre
de la ruta («application») y el segundo parámetro es el arreglo
de parámetros que se pasa al enrutador. Estos identifican la página web
a donde redirigimos el usuario.
Prepararemos la acción de controlador y la plantilla de vista para la página Gracias a ti un poco más adelante.
En la línea 37 pasamos el modelo de formulario a través de la variable $form
a la plantilla de vista. La plantilla de vista tendrá acceso a esta variable
y la usará para mostrar en pantalla el formulario (y los posibles errores de
validación).
Para dar un ejemplo realista de como usar la validación de datos en el formulario
de contacto crearemos en esta sección una clase de modelo simple llamada MailSender
24
que se usará para enviar un correo electrónico a una dirección de correo electrónico.
Cuando el usuario envía el formulario validamos los datos del formulario y pasamos
los datos validados al modelo MailServer
pidiéndole que envíe el correo electrónico
al destinatario.
24) En la terminología DDD MailSender
se puede considerar como un modelo
de servicio porque su objetivo es manipular datos y no guardar datos.
La lectura de esta sección es opcional y esta pensada fundamentalmente para principiantes. Podemos saltarla y revisar directamente la siguiente sección Form Presentation.
El modelo MailSender
usará internamente el componente Zend\Mail
. El componente
Zend\Mail
es un componente que provee Zend Framework 3 diseñado para proveer las
funcionalidades necesarias para construir mensajes de correo electrónico (la clase
Zend\Mail\Message
) y varias clases que implementan los diferentes transportes
para enviar correos electrónicos (en este ejemplo usaremos la clase
Zend\Mail\Transport\Sendmail
que usa el programa sendmail para enviar correos
electrónicos).
Instalamos el componente
Zend\Mail
con Composer escribiendo el siguiente comando:
php composer.phar require zendframework/zend-mail
El programa sendmail es un MTA (Mail Transfer Agent) de software libre para sistemas GNU/Linux y Unix. Este MTA acepta mensajes pasados por scripts de PHP y decide en base a la cabecera del mensaje que método de envío debe usar y luego pasa el mensaje mediante el protocolo SMTP al servidor de correo apropiado (como Google Mail) para enviarlo al destinatario.
Comenzaremos creando el archivo MailSender.php dentro de la carpeta Service que está dentro de la carpeta src del módulo (ver la figura 7.19).
El siguiente es el código que deberíamos colocar dentro del archivo MailSender.php:
<?php
namespace Application\Service;
use Zend\Mail;
use Zend\Mail\Message;
use Zend\Mail\Transport\Sendmail;
// This class is used to deliver an E-mail message to recipient.
class MailSender
{
// Sends the mail message.
public function sendMail($sender, $recipient, $subject, $text)
{
$result = false;
try {
// Create E-mail message
$mail = new Message();
$mail->setFrom($sender);
$mail->addTo($recipient);
$mail->setSubject($subject);
$mail->setBody($text);
// Send E-mail message
$transport = new Sendmail('-f'.$sender);
$transport->send($mail);
$result = true;
} catch(\Exception $e) {
$result = false;
}
// Return status
return $result;
}
}
En el código de arriba definimos el namespace Application\Service
(línea 2)
porque la clase MailSender
se comporta como un modelo de servicio (su objetivo
es manipular los datos y no guardarlos).
En las líneas 4-6 declaramos los alias para las clases Mail
, Message
y
Zend\Mail\Transport\Sendmail
que provee el componente Zend\Mail
.
En las líneas 9-35 definimos la clase MailSender
. La clase tiene un único
método sendMail()
(línea 12) que toma cuatro argumentos: remitente del
correo electrónico, destinatario del correo, el asunto del mensaje y el cuerpo
del mensaje.
En la línea 18 creamos una instancia de la clase Message
. Usamos los métodos
que provee esta clase para construir el mensaje (colocar el asunto, el cuerpo,
etc) en las líneas 19-22.
En la línea 25 creamos una instancia de la clase Sendmail
que usa el programa
sendmail para pasar el mensaje al servidor de correo apropiado (ver líneas 25-26).
Como las clases que provee el componente Zend\Mail
pueden lanzar una excepción
en el caso de un fallo encerramos el bloque de código entre el administrador de
excepciones try
-catch
.
El método sendMail()
regresará true
si el correo electrónico se envía
con éxito de lo contrario regresará false
(línea 33).
Configurar un sistema de correo para nuestro servidor web es una tarea que reviste algo de complejidad. Generalmente es necesario instalar sendmail y configurar el registro MX DNS del servidor para usar determinado servidor de correo (o también un servidor de correo local, por ejemplo, Postfix o un servidor remoto como Google Mail). Por la complejidad del problema este no se discute en este libro. Podemos encontrar en internet información adicional sobre la configuración de un servidor de correo electrónico para nuestra sistema en particular.
Ahora vamos a registrar el servicio MailSender
en nuestro archivo
module.config.php
de la siguiente manera:
return [
//...
'service_manager' => [
'factories' => [
Service\MailSender::class => InvokableFactory::class,
],
],
//...
];
Luego vamos a instanciar el modelo MailSender
en nuestro método
IndexController::contactUsAction()
y le pasaremos los datos del formulario
validado.
Como usamos el servicio
MailSender
en nuestro controlador este servicio es una dependencia de nuestro controlador. Así, necesitaremos crear una fábrica para el controlador e inyectar la dependencia dentro de constructor del controlador. Parece complejo a primera vista pero a medida que mejoremos nuestras habilidades encontraremos que esto más simple y que mejora enormemente la estructura del código.
Vamos a crear una fábrica para el IndexController
, la colocaremos dentro de la
subcarpeta Factory
que esta dentro de la subcarpeta Controller
. Como podemos
ver el único trabajo de la clase fábrica es crear el controlador y pasarle la
dependencia.
<?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);
}
}
Modificamos el archivo module.config.php
para usar la fábrica ajustada a la
medida que acabamos de crear:
return [
//...
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\Factory\IndexControllerFactory::class,
],
],
//...
];
Luego agregamos el constructor y los métodos contactUsAction()
, thankYouAction()
y sendErrorAction()
al controlador. Abajo mostramos todo el código:
<?php
// ...
use Application\Service\MailSender;
class IndexController extends AbstractActionController
{
private $mailSender;
public function __construct($mailSender)
{
$this->mailSender = $mailSender;
}
public function contactUsAction()
{
// Create Contact Us form
$form = new ContactForm();
// Check if user has submitted the form
if($this->getRequest()->isPost()) {
// Fill in the form with POST data
$data = $this->params()->fromPost();
$form->setData($data);
// Validate form
if($form->isValid()) {
// Get filtered and validated data
$data = $form->getData();
$email = $data['email'];
$subject = $data['subject'];
$body = $data['body'];
// Send E-mail
if(!$this->mailSender->sendMail('no-reply@example.com', $email,
$subject, $body)) {
// In case of error, redirect to "Error Sending Email" page
return $this->redirect()->toRoute('application',
['action'=>'sendError']);
}
// Redirect to "Thank You" page
return $this->redirect()->toRoute('application',
['action'=>'thankYou']);
}
}
// Pass form variable to view
return new ViewModel([
'form' => $form
]);
}
// This action displays the Thank You page. The user is redirected to this
// page on successful mail delivery.
public function thankYouAction()
{
return new ViewModel();
}
// This action displays the Send Error page. The user is redirected to this
// page on mail delivery error.
public function sendErrorAction()
{
return new ViewModel();
}
}
Como podemos ver en el código hacemos lo siguiente:
En la línea 3 declaramos un alias para la clase Application\Service\MailSender
.
Esto permitirá referirnos a la clase modelo por su nombre corto.
En las líneas 32-34 después de que hemos validado el formulario depositamos los
campos validados dentro de las variables de PHP $email
, $subject
y $body
.
En la línea 37 llamamos al método sendMail()
del servicio MailSender
y
le pasamos cuatro parámetros: la dirección de correo electrónico del remitente
(aquí usamos "no-reply@example.com" pero podemos remplazarlo con la dirección
de nuestro sendmail); la dirección de correo del remitente, el asunto y
cuerpo del correo.
Si el correo se envía con éxito (si el método sendMail()
regresa true
)
redirigimos al usuario a la página Gracias a ti (línea 45). En caso
de fallar (si el método sendMail()
regresa false
) redirigimos al
usuario a la página Enviar Error (línea 40).
En las líneas 58-61 tenemos al método thankYouAction()
que muestra la
página Gracias a ti. La página se muestra si el correo electrónico se
envía exitosamente.
En las líneas 65-68 tenemos el método sendErrorAction()
que muestra la
página Error al enviar el correo. Esta página se muestra cuando falla el
envío del correo electrónico.