Web hosting

«  [Parte 1] — Configurando Symfony2 y sus plantillas   ::   Contenido   ::   [Parte 3] — El modelo del Blog: Usando Doctrine 2 y accesorios  »

[Parte 2] — Página de contacto: Validadores, formularios y correo electrónico

Descripción

Ahora que tenemos en su lugar las plantillas HTML básicas, es hora de hacer una de las páginas funcionales. Vamos a empezar con una de las páginas más simples; La página de Contacto. Al final de este capítulo tendrás una página de Contacto que permite a los usuarios enviar sus consultas al administrador del sitio. Estas consultas serán enviadas por correo electrónico al administrador del sitio.

En este capítulo cubriremos las siguientes áreas:

  1. Validadores
  2. Formularios
  3. Ajuste de los valores de configuración del paquete

Página de contacto

Enrutando

Al igual que con la página sobre creada en el capítulo anterior, vamos a comenzar definiendo la ruta de la página de Contacto. Abre el archivo de enrutado del BloggerBlogBundle ubicado en src/Blogger/BlogBundle/Resources/config/routing.yml y añade la siguiente regla de enrutado.

# src/Blogger/BlogBundle/Resources/config/routing.yml
BloggerBlogBundle_contact:
    pattern:  /contact
    defaults: { _controller: BloggerBlogBundle:Page:contact }
    requirements:
        _method:  GET

No hay nada nuevo aquí, la regla coincide con el patrón /contact, para el método GET del protocolo HTTP y ejecuta la acción contact del controlador Page en el BloggerBlogBundle.

Controlador

A continuación vamos a añadir la acción para la página Contacto al controlador Page en el BloggerBlogBundle situado en src/Blogger/BlogBundle/Controller/PageController.php.

// src/Blogger/BlogBundle/Controller/PageController.php
// ..
public function contactAction()
{
    return $this->render('BloggerBlogBundle:Page:contact.html.twig');
}
// ..

Por ahora la acción es muy simple, sólo reproduce la vista de la página contacto. Más tarde volveremos al controlador.

La vista

Crea la vista de la página contacto en src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig y añade el siguiente contenido.

{# src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig #}
{% extends 'BloggerBlogBundle::base.html.twig' %}

{% block title %}Contact{% endblock%}

{% block body %}
    <header>
        <h1>Contact symblog</h1>
    </header>

    <p>Want to contact symblog?</p>
{% endblock %}

Esta plantilla también es muy simple. Extiende la plantilla del diseño de BloggerBlogBundle, remplazando el bloque de título para establecer un título personalizado y define algún contenido para el bloque body.

Enlazando la página

Por último tenemos que actualizar el enlace en la plantilla de la aplicación ubicada en app/Resources/views/base.html.twig para enlazar la página de contacto.

<!-- app/Resources/views/base.html.twig -->
{% block navigation %}
    <nav>
        <ul class="navigation">
            <li><a href="{{ path('BloggerBlogBundle_homepage') }}">Home</a></li>
            <li><a href="{{ path('BloggerBlogBundle_about') }}">About</a></li>
            <li><a href="{{ path('BloggerBlogBundle_contact') }}">Contact</a></li>
        </ul>
    </nav>
{% endblock %}

Si diriges tu navegador a http://symblog.dev/app_dev.php/ y haces clic en el enlace de contacto en la barra de navegación, deberías ver una página de contacto muy básica. Ahora que hemos configurado la página correctamente, es hora de empezar a trabajar en el formulario de Contacto. Esto se divide en dos partes bien diferenciadas; Los validadores y el formulario. Antes de que podamos abordar el concepto de los validadores y el formulario, tenemos que pensar en cómo vamos a manejar los datos de la consulta de Contacto.

La entidad Contacto

Vamos a empezar creando una clase que representa una consulta de Contacto de un usuario. Queremos capturar información básica como nombre, asunto y el cuerpo de la consulta. Crea un nuevo archivo situado en src/Blogger/BlogBundle/Entity/Enquiry.php y pega el siguiente contenido:

<?php
// src/Blogger/BlogBundle/Entity/Enquiry.php

namespace Blogger\BlogBundle\Entity;

class Enquiry
{
    protected $name;

    protected $email;

    protected $subject;

    protected $body;

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }

    public function getSubject()
    {
        return $this->subject;
    }

    public function setSubject($subject)
    {
        $this->subject = $subject;
    }

    public function getBody()
    {
        return $this->body;
    }

    public function setBody($body)
    {
        $this->body = $body;
    }
}

Como puedes ver esta clase sólo define algunas de las propiedades protegidas y métodos para acceder a ellas. No hay nada aquí que defina cómo validar las propiedades, o cómo se relacionan las propiedades con los elementos de formulario. Volveremos a esto más adelante.

Nota

Vamos a desviarnos un poco para hablar rápidamente sobre el uso de los espacios de nombres en Symfony2. La clase entidad que hemos creado establece el espacio de nombres Blogger\BlogBundle\Entity. Puesto que Symfony2 es compatible con la carga automática del estándar PSR-0 el espacio de nombres denota la estructura de directorios del paquete. La clase entidad Enquiry se encuentra en src/Blogger/BlogBundle/Entity/Enquiry.php lo cual garantiza que Symfony2 está en condiciones de cargar la clase automática y correctamente.

¿Cómo hace el cargador automático de Symfony2 para saber que el espacio de nombres del Blogger se puede encontrar en el directorio src? Esto es gracias a los ajustes en el cargador automático en app/autoloader.php

// app/autoloader.php
$loader->registerNamespaceFallbacks(array(
    __DIR__.'/../src',
));

Esta expresión registra un retroceso para cualquier espacio de nombres que no esté registrado ya. Debido a que el espacio de nombres del Blogger no está registrado, el cargador automático de Symfony2 buscará los archivos necesarios en el directorio src.

La carga automática y el espacio de nombres son conceptos muy potentes en Symfony2. Si se producen errores donde PHP es incapaz de encontrar las clases, es probable que haya un error en el espacio de nombres o la estructura de directorios. También puedes verificar el espacio de nombres que se ha registrado en el cargador automático como se muestra arriba. Nunca debes ceder a la tentación de “corregir” esto usando las directivas PHP require o include.

Formularios

A continuación vamos a crear el formulario. Symfony2 viene empacado con una plataforma de formularios muy potente que facilita la tediosa tarea de tratar con formularios. Como con todo los componentes de Symfony2, lo puedes utilizar fuera de Symfony2 en tus propios proyectos. El código fuente del componente Form está disponible en Github. Vamos a empezar creando una clase AbstractType que representa el formulario de la consulta. Podríamos haber creado el formulario directamente en el controlador y no molestarnos con esta clase, sin embargo, separar el formulario en su propia clase nos permite volver a utilizar el formulario a través de la aplicación. También nos evita saturar el controlador. Después de todo, se supone que el controlador es simple. Su propósito es proporcionar el pegamento entre el Modelo y la Vista.

EnquiryType

Crea un nuevo archivo situado en `src/Blogger/BlogBundle/Form/EnquiryType.php y pega el siguiente contenido:

<?php
// src/Blogger/BlogBundle/Form/EnquiryType.php

namespace Blogger\BlogBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class EnquiryType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name');
        $builder->add('email', 'email');
        $builder->add('subject');
        $builder->add('body', 'textarea');
    }

    public function getName()
    {
        return 'contact';
    }
}

La clase EnquiryType introduce la clase FormBuilder. La clase FormBuilder es tu mejor amiga cuando se trata de crear formularios. Esta es capaz de simplificar el proceso de definición de campos basándose en los metadatos con que cuenta el campo. Debido a que nuestra entidad Enquiry es tan simple aún no hemos definido los metadatos para el FormBuilder los cuales por omisión tienen el tipo de campo de entrada de texto. Esto es conveniente para la mayoría de los campos, excepto para el cuerpo, para el cual queremos un textarea, y el email donde deseamos tomar ventaja del nuevo tipo de entrada email de HTML5.

Nota

Un punto clave a mencionar aquí es que el método getName debe devolver un identificador único.

Creando el formulario en el controlador

Ahora que hemos definido la entidad Enquiry y el tipo EnquiryType, podemos actualizar la acción Contacto para usarlas. Remplaza el contenido de la acción Contacto ubicada en src/Blogger/BlogBundle/Controller/PageController.php con lo siguiente:

// src/Blogger/BlogBundle/Controller/PageController.php
public function contactAction()
{
    $enquiry = new Enquiry();
    $form = $this->createForm(new EnquiryType(), $enquiry);

    $request = $this->getRequest();
    if ($request->getMethod() == 'POST') {
        $form->bindRequest($request);

        if ($form->isValid()) {
            // realiza alguna acción, como enviar un correo electrónico

            // Redirige - Esto es importante para prevenir que el usuario
            // reenvíe el formulario si actualiza la página
            return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
        }
    }

    return $this->render('BloggerBlogBundle:Page:contact.html.twig', array(
        'form' => $form->createView()
    ));
}

Empezamos creando una instancia de la entidad Enquiry. Esta entidad representa los datos de una consulta de contacto. A continuación, creamos el formulario real. Especificamos el EnquiryType que creamos antes, y le pasamos nuestro objeto entidad Enquiry. El método createForm es capaz de utilizar estos dos indicios para crear una representación del formulario.

Ya que esta acción del controlador tratará de mostrar y procesar el formulario presentado, tenemos que verificar el método HTTP. Los formularios presentados se suelen enviar a través del método POST, y nuestro formulario no será la excepción. Si el método de la petición es POST, una llamada a bindRequest transformará los datos presentados de nuevo a las propiedades de nuestro objeto $enquiry. En este punto el objeto $enquiry ahora tiene una representación de lo que el usuario envió.

A continuación hacemos una comprobación para ver si el formulario es válido. Debido a que no hemos especificado ningún validador en este punto, el formulario siempre será válido.

Finalmente especificamos la plantilla a reproducir. Ten en cuenta que ahora le estamos pasando a la plantilla una representación de la vista del formulario. Este objeto nos permite reproducir el formulario en la vista.

Debido a que hemos utilizado dos nuevas clases en nuestro controlador, necesitamos importar los espacios de nombres. Actualiza el archivo controlador que se encuentra en src/Blogger/BlogBundle/Controller/PageController.php con lo siguiente. Las declaraciones use se deben colocar bajo las use existentes.

<?php
// src/Blogger/BlogBundle/Controller/PageController.php

namespace Blogger\BlogBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
// Importa el nuevo espacio de nombres
use Blogger\BlogBundle\Entity\Enquiry;
use Blogger\BlogBundle\Form\EnquiryType;

class PageController extends Controller
// ..

Reproduciendo el formulario

Gracias a los métodos de reproducción de formularios de Twig es muy simple. Twig proporciona un sistema de capas para representar formularios, el cual te permite reproducir el formulario como una entidad completa, o como errores individuales y elementos, dependiendo del nivel de personalización que requieras.

Para demostrar el poder de los métodos de Twig puedes utilizar el siguiente fragmento de código para reproducir el formulario completo.

<form action="{{ path('BloggerBlogBundle_contact') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}

    <input type="submit" />
</form>

Si bien esto es muy útil para formularios simples y prototipos, tiene sus limitaciones cuando necesitas personalización extendida, que a menudo es el caso con los formularios.

Para nuestro formulario de contacto, vamos a optar por un término medio. Reemplaza el código de la plantilla ubicada en src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig con el siguiente.

{# src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig #}
{% extends 'BloggerBlogBundle::base.html.twig' %}

{% block title %}Contact{% endblock%}

{% block body %}
    <header>
        <h1>Contact symblog</h1>
    </header>

    <p>Want to contact symblog?</p>

    <form action="{{ path('BloggerBlogBundle_contact') }}" method="post" {{ form_enctype(form) }} class="blogger">
        {{ form_errors(form) }}

        {{ form_row(form.name) }}
        {{ form_row(form.email) }}
        {{ form_row(form.subject) }}
        {{ form_row(form.body) }}

        {{ form_rest(form) }}

        <input type="submit" value="Submit" />
    </form>
{% endblock %}

Como puedes ver, utilizamos cuatro nuevos métodos de Twig para reproducir el formulario.

El primer método form_enctype establece el tipo de contenido del formulario. Este se debe establecer cuando tu formulario trata con la subida de archivos. Nuestro formulario no tiene ningún uso para este método, pero es buena práctica utilizarlo siempre en todos tus formularios en caso de que puedas agregar la carga de archivos en el futuro. Depurar un formulario que gestiona la carga de archivos y que no tiene establecido el tipo de contenido, ¡se puede convertir en un verdadero rascadero de cabeza!

El segundo método form_errors reproducirá cualquier error del formulario en caso de que la validación haya fallado.

El tercer método form_row reproduce todos los elementos relacionados con cada campo del formulario. Esto incluye los errores del campo, la etiqueta label para el campo y el elemento gráfico real del campo.

Por último utilizamos el método form_rest. El cual siempre es una apuesta segura para utilizar el método al final del formulario para reproducir los campos que puedas haber olvidado, incluidos los campos ocultos y el segmento CSRF de los formularios de Symfony2.

Nota

La falsificación de petición en sitios cruzados (CSRF) se explica con detalle en el capítulo Formularios del libro de Symfony2.

Estilizando el formulario

Si ves el formulario de contacto en http://symblog.dev/app_dev.php/contact te darás cuenta de que no se ve tan atractivo. Le vamos a añadir algo de estilo para mejorar ese aspecto. Puesto que los estilos son específicos al formulario dentro de nuestro paquete de Blog vamos a crear los estilos en una hoja de estilo dentro del propio paquete. Crea un nuevo archivo ubicado en src/Blogger/BlogBundle/Resources/public/css/blog.css y pega el siguiente contenido:

.blogger-notice { text-align: center; padding: 10px; background: #DFF2BF; border: 1px solid; color: #4F8A10; margin-bottom: 10px; }
form.blogger { font-size: 16px; }
form.blogger div { clear: left; margin-bottom: 10px; }
form.blogger label { float: left; margin-right: 10px; text-align: right; width: 100px; font-weight: bold; vertical-align: top; padding-top: 10px; }
form.blogger input[type="text"],
form.blogger input[type="email"]
    { width: 500px; line-height: 26px; font-size: 20px; min-height: 26px; }
form.blogger textarea { width: 500px; height: 150px; line-height: 26px; font-size: 20px; }
form.blogger input[type="submit"] { margin-left: 110px; width: 508px; line-height: 26px; font-size: 20px; min-height: 26px; }
form.blogger ul li { color: #ff0000; margin-bottom: 5px; }

Tenemos que hacerle saber a la aplicación que queremos utilizar esta hoja de estilos. Podríamos importar la hoja de estilos en la plantilla de contacto, pero, debido a que más tarde o más temprano, otras plantillas también utilizarán esta hoja de estilos, tiene sentido importarla en el diseño del BloggerBlogBundle que creamos en el capítulo 1. Abre el diseño de src/Blogger/BlogBundle/Resources/views/base.html.twig y sustitúyelo con el siguiente contenido:

{# src/Blogger/BlogBundle/Resources/views/base.html.twig #}
{% extends '::base.html.twig' %}

{% block stylesheets %}
    {{ parent() }}
    <link href="{{ asset('bundles/bloggerblog/css/blog.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}

{% block sidebar %}
    Sidebar content
{% endblock %}

Puedes ver que hemos definido un bloque de hojas de estilo para sustituir el bloque de hojas de estilo definido en la plantilla padre. Sin embargo, es importante que tengas en cuenta la llamada al método parent(). Este importará el contenido del bloque de las hojas de estilo en la plantilla padre ubicada en app/Resources/base.html.twig, lo cual nos permite añadir nuestra nueva hoja de estilos. Después de todo, no deseamos reemplazar el estilo existente.

A fin de que la función asset vincule correctamente los recursos, necesitamos copiar o vincular los recursos en el paquete al directorio web de la aplicación. Esto lo puedes hacer con la siguiente orden:

$ php app/console assets:install web --symlink

Nota

Si estás usando un sistema operativo que no es compatible con enlaces simbólicos, tal como Windows, tendrás que olvidarte de la opción de enlace simbólicos de la siguiente manera.

php app/console assets:install web

Este método en realidad va a copiar los recursos desde los directorios public de todos los paquetes al directorio web de la aplicación. Puesto que los archivos se copian en realidad, será necesario ejecutar esta tarea cada vez que realices un cambio a un recurso público en cualquiera de tus paquetes.

Ahora bien, si actualizas la página del formulario de contacto estará bellamente decorada.

formulario de contacto de symblog

Truco

Si bien la función asset proporciona la funcionalidad que necesitas para usar tus recursos, hay una mejor alternativa para esto. La biblioteca Assetic de Kris Wallsmith incluida de manera predeterminada en la edición estándar de Symfony2. Esta biblioteca proporciona una gestión de activos más allá de las capacidades estándar de Symfony2. Assetic nos permite ejecutar filtros en los activos para combinarlos automáticamente, minifyzarlos y comprimirlos con gzip. También puede ejecutar filtros de compresión en imágenes. Además Assetic nos permite hacer referencia a los recursos directamente en los directorios public de los paquetes sin tener que ejecutar la tarea assets:install. Exploraremos el uso de Assetic en capítulos posteriores.

Fallo en la presentación

Si no pudiste reprimir tus ganas de enviar el formulario serás recibido con un error de Symfony2.

No se encontró una ruta "POST para /contact": Método no permitido (Permite: GET, HEAD)

Este error nos está diciendo que no hay una ruta que coincida con /contact para el método POST de HTTP. La ruta sólo acepta peticiones GET y HEAD. Esto se debe a que configuramos la ruta con el requisito del método GET.

Actualicemos la ruta de contacto ubicada en src/Blogger/BlogBundle/Resources/config/routing.yml para permitir también las peticiones POST.

# src/Blogger/BlogBundle/Resources/config/routing.yml
BloggerBlogBundle_contact:
    pattern:  /contact
    defaults: { _controller: BloggerBlogBundle:Page:contact }
    requirements:
        _method:  GET|POST

Truco

Tal vez estés preguntándote por qué la ruta permitiría el método HEAD cuando sólo hemos especificado GET. Esto es porque HEAD es una petición GET, pero sólo se devuelven las cabeceras HTTP.

Ahora, cuando envíes el formulario deberá funcionar como se esperaba, aunque en realidad no esperes que haga mucho todavía. La página sólo te regresará al formulario de contacto.

Validadores

Los validadores de Symfony2 nos permiten realizar la tarea de validación de datos. La validación es una tarea común cuando se trata de datos de formularios. Asimismo, la validación se debe realizar en los datos antes de guardarlos en una base de datos. El validador de Symfony2 nos permite separar nuestra lógica de validación fuera de los componentes que puedas utilizar, como el componente formulario o el componente de base de datos. Este enfoque significa que tenemos un conjunto de reglas de validación para un objeto.

Vamos a empezar actualizando la entidad Enquiry ubicada en src/Blogger/BlogBundle/Entity/Enquiry.php para especificar algunos validadores. Asegúrate de añadir las 5 nuevas declaraciones use en la parte superior del archivo:

<?php
// src/Blogger/BlogBundle/Entity/Enquiry.php

namespace Blogger\BlogBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\MaxLength;

class Enquiry
{
    // ..

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('name', new NotBlank());

        $metadata->addPropertyConstraint('email', new Email());

        $metadata->addPropertyConstraint('subject', new NotBlank());
        $metadata->addPropertyConstraint('subject', new MaxLength(50));

        $metadata->addPropertyConstraint('body', new MinLength(50));
    }

    // ..

}

Para definir los validadores debemos implementar el método estático loadValidatorMetadata. Este nos proporciona un objeto ClassMetadata. Podemos utilizar este objeto para poner restricciones a las propiedades de nuestra entidad. La primera declaración aplica la restricción NotBlank a la propiedad name. El validador NotBlank es tan simple como suena, sólo devolverá true si el valor a comprobar no está vacío. A continuación configuramos la validación para la propiedad email. El servicio Validador de Symfony2 proporciona un validador para direcciones de correo electrónico el cual incluso revisará los registros MX para garantizar que el dominio es válido. En la propiedad subject deseamos establecer una restricción NotBlank y una MaxLength. Puedes aplicar a una propiedad tantos validadores como desees.

En los documentos de referencia de Symfony2 hay una lista completa de las restricciones de validación. También es posible crear restricciones de validación personalizadas.

Ahora, cuando envíes el formulario de contacto, los datos presentados se pasan a través de las restricciones de validación. Inténtalo escribiendo una dirección de correo electrónico incorrecta. Deberías ver un mensaje de error informándote que la dirección de correo electrónico no es válida. Cada validador proporciona un mensaje predeterminado el cual puedes utilizar —de ser necesario— para remplazarlo. Para cambiar el mensaje producido por el validador de correo electrónico tienes que hacer lo siguiente:

$metadata->addPropertyConstraint('email', new Email(array(
    'message' => 'symblog does not like invalid emails. Give me a real one!'
)));

Truco

Si estás utilizando un navegador compatible con HTML5 (lo cual es lo más probable) se te informará con los mensajes de HTML5 los cuales fuerzan determinadas restricciones. Esta es la validación del lado del cliente y Symfony2 establecerá las restricciones HTML5 apropiadas basándose en los metadatos de tu entidad. Esto lo puedes ver en el elemento de correo electrónico. La salida HTML es:

<input type="email" value="" required="required" name="contact[email]" id="contact_email">

Este ha utilizado uno de los nuevos tipos de campo de entrada HTML5, email y ha establecido el atributo necesario. La validación del lado del cliente es grandiosa, ya que no requiere un viaje de vuelta al servidor para validar el formulario. Sin embargo, no debes usar solo la validación del lado del cliente. Siempre debes validar los datos presentados en el servidor, debido a que es bastante fácil para un usuario malintencionado eludir la validación del lado del cliente.

Enviando correo electrónico

Aunque nuestro formulario de contacto permitirá a los usuarios enviar consultas, realmente nada sucede con ellos todavía. Actualicemos el controlador para enviar un correo electrónico al administrador del blog. Symfony2 viene con la biblioteca Swift Mailer completa para enviar mensajes de correo electrónico. Swift Mailer es una biblioteca muy potente; Sólo vislumbraremos la superficie de lo que esta biblioteca puede realizar.

Configurando las opciones de Swift Mailer

Swift Mailer ya está configurado fuera de la caja para trabajar en la edición estándar de Symfony2, sin embargo tenemos que configurar algunos parámetros relacionados a los métodos de envío, y las credenciales. Abre el archivo de parámetros situado en app/config/parameters.yml y encuentra las opciones prefijadas con mailer_.

mailer_transport="smtp"
mailer_host="localhost"
mailer_user=""
mailer_password=""

Swift Mailer proporciona una serie de métodos para enviar correos electrónicos, incluyendo el uso de un servidor SMTP, utilizando una instalación local de sendmail, o incluso con una cuenta de GMail. Por simplicidad vamos a utilizar una cuenta de GMail. Actualiza los parámetros con lo siguiente, sustituyendo tu nombre de usuario y contraseña cuando sea necesario.

mailer_transport="gmail"
mailer_encryption="ssl"
mailer_auth_mode="login"
mailer_host="smtp.gmail.com"
mailer_user="tu_nombre_de_usuario"
mailer_password="tu_contraseña"

Advertencia

Ten cuidado si estás usando un sistema de control de versiones (CVS) como Git para tu proyecto, especialmente si es repositorio de acceso público, puesto que tu nombre de usuario y contraseña de Gmail serán enviados al repositorio, y estarán disponibles para que cualquiera los vea. Debes asegurarte de agregar el archivo app/config/parameters.yml a la lista de ignorados de tu CVS. Un enfoque común a este problema es añadir un sufijo al nombre del archivo que contiene información sensible, tal como app/config/parameters.yml con .dist. A continuación, proporcionas los parámetros predeterminados para las opciones de este archivo y agregas el archivo real, es decir, app/config/parameters.yml a la lista de ignorados por tu CVS. A continuación, puedes desplegar el archivo *.dist con tu proyecto y permitir al desarrollador eliminar la extensión .dist y cumplimentar los ajustes necesarios.

Actualizando el controlador

Actualiza el controlador Page que se encuentra en src/Blogger/BlogBundle/Controller/PageController.php con el siguiente contenido:

// src/Blogger/BlogBundle/Controller/PageController.php

public function contactAction()
{
    // ..
    if ($form->isValid()) {

        $message = \Swift_Message::newInstance()
            ->setSubject('Contact enquiry from symblog')
            ->setFrom('enquiries@symblog.co.uk')
            ->setTo('email@email.com')
            ->setBody($this->renderView('BloggerBlogBundle:Page:contactEmail.txt.twig', array('enquiry' => $enquiry)));
        $this->get('mailer')->send($message);

        $this->get('session')->setFlash('blogger-notice', 'Your contact enquiry was successfully sent. Thank you!');

        // Redirige - Esto es importante para prevenir que el usuario
        // reenvíe el formulario si actualiza la página
        return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
    }
    // ..
}

Cuando utilizas la biblioteca Swift Mailer para crear una instancia de Swift_Message, la puedes enviar como correo electrónico.

Nota

Debido a que la biblioteca Swift Mailer no utiliza espacios de nombres, es necesario prefijar la clase Swift Mailer con una \. Esto le indica a PHP que escape de nuevo al espacio global. Será necesario que prefijes todas las clases y funciones que no están en un espacio de nombres con una \. Si no colocas este prefijo antes de la clase Swift_Message, PHP debería buscar la clase en el espacio de nombres actual, que en este ejemplo es Blogger\BlogBundle\Controller, provocando que se lance un error.

También hemos establecido un mensaje flash en la sesión. Los mensajes flash son mensajes que persisten durante exactamente una petición. Después de eso, Symfony2 los limpia automáticamente. El mensaje flash se mostrará en la plantilla de contacto para informar al usuario que se ha enviado la consulta. Como los mensajes flash sólo persisten durante exactamente una petición, son perfectos para notificar al usuario del éxito de las acciones anteriores.

Para mostrar el mensaje flash tenemos que actualizar la plantilla de contacto situada en src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig. Actualiza el contenido de la plantilla con lo siguiente:

{# src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig #}

{# resto de la plantilla ... #}
<header>
    <h1>Contact symblog</h1>
</header>

{% if app.session.hasFlash('blogger-notice') %}
    <div class="blogger-notice">
        {{ app.session.flash('blogger-notice') }}
    </div>
{% endif %}

<p>Want to contact symblog?</p>

{# resto de la plantilla ... #}

Esto comprueba si hay un mensaje flash con el identificador 'blogger-notice' y, de existir, lo devuelve.

Registrando el correo electrónico del administrador

Symfony2 ofrece un sistema de configuración que podemos utilizar para definir nuestros propios valores. Vamos a utilizar este sistema para establecer la dirección de correo electrónico del administrador del sitio web en lugar de la codificación fija del controlador de arriba. De esa forma puedes volver a usar este valor en otros lugares, sin duplicar tu código. Además, cuando tu blog ha generado mucho tráfico las consultas son demasiadas para que puedas procesarlas actualizando fácilmente la dirección de correo electrónico para transmitir los mensajes a tu asistente. Crea un nuevo archivo en src/Blogger/BlogBundle/Resources/config/config.yml y pega lo siguiente:

# src/Blogger/BlogBundle/Resources/config/config.yml
parameters:
    # dirección de correo electrónico para contacto del Blogger
    blogger_blog.emails.contact_email: contact@email.com

Al definir parámetros es una buena práctica romper el nombre del parámetro en una serie de componentes. La primera parte debe ser una versión en minúsculas del nombre del paquete con un subrayado para separar las palabras. En nuestro ejemplo, hemos transformado el paquete BloggerBlogBundle a blogger_blog. El resto del nombre del parámetro puede contener cualquier número de partes separadas por un carácter de . (punto). Esto nos permite agrupar lógicamente los parámetros.

A fin de que la aplicación Symfony2 utilice los nuevos parámetros, tenemos que importar los ajustes en el archivo de configuración principal de la aplicación ubicado en app/config/config.yml. Para lograrlo actualiza la directiva imports en la parte superior del archivo a lo siguiente:

# app/config/config.yml
imports:
    # .. aquí la importación existente
    - { resource: @BloggerBlogBundle/Resources/config/config.yml }

La ruta de importación es la ubicación física del archivo en el disco. La directiva @BloggerBlogBundle se resolverá en la ruta del BloggerBlogBundle que es src/Blogger/BlogBundle.

Finalmente actualicemos la acción Contacto para utilizar el parámetro.

// src/Blogger/BlogBundle/Controller/PageController.php

public function contactAction()
{
    // ..
    if ($form->isValid()) {

        $message = \Swift_Message::newInstance()
            ->setSubject('Contact enquiry from symblog')
            ->setFrom('enquiries@symblog.co.uk')
            ->setTo($this->container->getParameter('blogger_blog.emails.contact_email'))
            ->setBody($this->renderView('BloggerBlogBundle:Page:contactEmail.txt.twig', array('enquiry' => $enquiry)));
        $this->get('mailer')->send($message);

        // ..
    }
    // ..
}

Truco

Puesto que el archivo de ajustes se importa en la parte superior del archivo de configuración de la aplicación, fácilmente puedes reemplazar cualquiera de los parámetros importados en la aplicación. Por ejemplo, añadiendo lo siguiente en la parte inferior de app/config/config.yml para redefinir el valor establecido para el parámetro.

# app/config/config.yml
parameters:
    # dirección de correo electrónico para contacto con el Blogger
    blogger_blog.emails.contact_email: assistant@email.com

Esta personalización nos permite proporcionar parámetros predeterminados razonables para valores del paquete que la aplicación puede sustituir.

Nota

Si bien es fácil crear parámetros de configuración para el paquete utilizando este método, Symfony2 también proporciona un método en el que expones la configuración semántica de un paquete. Vamos a explorar este método más adelante en la guía.

Creando la plantilla para un correo electrónico

El cuerpo del correo electrónico se determina en una plantilla para reproducirla. Crea esa plantilla en src/Blogger/BlogBundle/Resources/views/Page/contactEmail.txt.twig y agrégale lo siguiente:

{# src/Blogger/BlogBundle/Resources/views/Page/contactEmail.txt.twig #}
A contact enquiry was made by {{ enquiry.name }} at {{ "now" | date("Y-m-d H:i") }}.

Reply-To: {{ enquiry.email }}
Subject: {{ enquiry.subject }}
Body:
{{ enquiry.body }}

El contenido del correo electrónico es sólo la consulta que envía el usuario.

Posiblemente también hayas notado que la extensión de esta plantilla es diferente a las otras plantillas que hemos creado. Esta utiliza la extensión .txt.twig. La primera parte de la extensión .txt especifica el formato del archivo a generar. Los formatos más comunes aquí son, .txt, .html, .css, .js, .xml y .json. La última parte de la extensión especifica el motor de plantillas a usar, en este caso, Twig. Una extensión de .php debería usar PHP para reproducir la plantilla.

Ahora, cuando envíes una consulta, se enviará un correo electrónico a la dirección indicada en el parámetro blogger_blog.emails.contact_email.

Truco

Symfony2 nos permite configurar el comportamiento de Swift Mailer, mientras que la biblioteca opera en diferentes entornos de Symfony2. Ya lo podemos ver en acción en el entorno test. Por omisión, la edición estándar de Symfony2 configura el Swift Mailer para no enviar correos electrónicos cuando se ejecuta en el entorno test. Esto se establece en el archivo de configuración de pruebas ubicado en app/config/config_test.yml.

# app/config/config_test.yml
swiftmailer:
    disable_delivery: true

Tal vez sería útil duplicar esta funcionalidad en el entorno dev. Después de todo, durante el desarrollo, no deseas enviar accidentalmente un correo electrónico a la dirección de correo electrónico incorrecta. Para lograr esto, añade la configuración anterior al archivo de configuración dev ubicado en app/config/config_dev.yml.

Tal vez te estés preguntando ¿ahora cómo puedo probar el envío de los correos electrónicos y específicamente su contenido, en vista de que ya no serán entregados a una dirección de correo electrónico real? Symfony2 tiene una solución para esto a través de la barra de depuración web. Cuando se le envía un correo electrónico aparecerá un icono de notificación de correo electrónico en la barra de depuración web, el cual tiene toda la información sobre el correo electrónico que Swift Mailer habría entregado.

La barra de herramientas de *Symfony2* mostrando notificaciones de correo electrónico

Si realizas una redirección después de enviar un correo electrónico, al igual que lo hicimos con el formulario de contacto, tendrás que establecer en true la opción intercept_redirects en app/config/config_dev.yml para ver la notificación de correo electrónico en la barra de depuración web.

En su lugar, podríamos haber configurado Swift Mailer para enviar todos los correos electrónicos a una dirección de correo electrónico específica en el entorno dev colocando la siguiente configuración en el archivo de configuración dev ubicado en app/config/config_dev.yml.

# app/config/config_dev.yml
swiftmailer:
    delivery_address:  development@symblog.dev

Conclusión

Hemos demostrado los conceptos detrás de la creación de una de las partes más fundamental de cualquier sitio web; los formularios. Symfony2 viene completo con una excelente biblioteca para validación de formularios y nos permite separar la lógica de validación del formulario para poder utilizarlo en otras partes de la aplicación (tal como el modelo). Además introdujimos los parámetros de configuración personalizados que puede leer nuestra aplicación.

A continuación vamos a ver una gran parte de esta guía, el Modelo. Introduciremos Doctrine 2 y lo utilizaremos para definir el modelo del blog. También vamos a crear la página para mostrar el blog y explorar el concepto de accesorios.

comentarios del blog accionados por Disqus

«  [Parte 1] — Configurando Symfony2 y sus plantillas   ::   Contenido   ::   [Parte 3] — El modelo del Blog: Usando Doctrine 2 y accesorios  »