Symfony

RECU-0263 (Recurso Referencia)

Descripción

Symfony es un framework cuyo principal objetivo es simplificar el desarrollo de aplicaciones en PHP mediante la automatización de algunos de los patrones utilizados para resolver las tareas comunes. Dado la normalización y estructuración que introduce el framework en el desarrollo, una de las ventajas que rápidamente se observan es un aumento en código más legible y más fácil de mantener. Por último, un framework facilita la programación de aplicaciones, ya que encapsula operaciones complejas en instrucciones sencillas.

Con este framework, es posible optimizar el desarrollo de aplicaciones web. Permite la separación de la lógica de negocio y la lógica del servidor, así como la capa de presentación de la aplicación. Symfony aporta diversas herramientas y clases con el objetivo de reducir el tiempo de desarrollo de una aplicación web compleja. Proporciona automatismos para las tareas más comunes, permitiendo al desarrollador dedicarse por completo a los aspectos específicos de cada aplicación.

Symfony está desarrollado completamente con PHP 5. Ha sido probado en numerosos proyectos reales y se utiliza en sitios web de comercio electrónico de primer nivel. Symfony es compatible con la mayoría de gestores de bases de datos, como MySQL, PostgreSQL, Oracle y SQL Server de Microsoft. Se puede ejecutar tanto en plataformas nix (Unix, Linux, etc.) como en plataformas Windows. A continuación se muestran algunas de sus características. Symfony se diseñó para que se ajustara a los siguientes requisitos:

  • Tiene un proceso de instalación y configuración bastante sencillo.
  • No tiene dependencias con un gestor de base de datos determinado.
  • Es muy adaptable a los caso más complejos de negocio.
  • Basado en la premisa de "convenir en vez de configurar", en la que el desarrollador sólo debe configurar aquello que no es convencional.
  • Sigue la mayoría de mejores prácticas y patrones de diseño para la web.
  • Estructura el código de manera que resulta fácil de leer ya que incluye comentarios de phpDocumentor, lo que mejora de forma sensible, el mantenimiento del código.
  • Fácil de extender, lo que permite su integración con librerías desarrolladas por terceros.

La implementación del MVC que realiza Symfony

Piense por un momento cuántos componentes se necesitarían para realizar una página sencilla que muestre un listado de las entradas o artículos de un blog. Son necesarios los siguientes componentes:

  • La capa del Modelo
    • Abstracción de la base de datos
    • Acceso a los datos
  • La capa de la Vista
    • Vista
    • Plantilla
    • Layout
  • La capa del Controlador
    • Controlador frontal
    • Acción

En total son siete scripts, lo que parecen muchos archivos para abrir y modificar cada vez que se crea una página. Afortunadamente, Symfony simplifica este proceso. Symfony toma lo mejor de la arquitectura MVC y la implementa de forma que el desarrollo de aplicaciones sea rápido y sencillo.

En primer lugar, el controlador frontal y el layout son comunes para todas las acciones de la aplicación. Se pueden tener varios controladores y varios layouts, pero solamente es obligatorio tener uno de cada. El controlador frontal es un componente que sólo tiene código relativo al MVC, por lo que no es necesario crear uno, ya que Symfony lo genera de forma automática.

La otra buena noticia es que las clases de la capa del modelo también se generan automáticamente, en función de la estructura de datos de la aplicación. La librería Propel se encarga de esta generación automática, ya que crea el esqueleto o estructura básica de las clases y genera automáticamente el código necesario. Cuando Propel encuentra restricciones de claves foráneas (o externas) o cuando encuentra datos de tipo fecha, crea métodos especiales para acceder y modificar esos datos, por lo que la manipulación de datos se convierte en algo más sencillo.

La abstracción de la base de datos es completamente transparente para el programador, ya que se realiza de forma nativa mediante PDO (PHP Data Objects). Así, si se cambia el sistema gestor de bases de datos en cualquier momento, no se debe reescribir ni una línea de código, ya que tan sólo es necesario modificar un parámetro en un archivo de configuración.

Por último, la lógica de la vista se puede transformar en un archivo de configuración sencillo, sin necesidad de programarla.

El Controlador

En Symfony, la capa del controlador, que contiene el código que liga la lógica de negocio con la presentación, está dividida en varios componentes que se utilizan para diversos propósitos:

  • El controlador frontal es el único punto de entrada a la aplicación. Carga la configuración y determina la acción a ejecutarse.
  • Las acciones contienen la lógica de la aplicación. Verifican la integridad de las peticiones y preparan los datos requeridos por la capa de presentación.
  • Los objetos request, response y session dan acceso a los parámetros de la petición, las cabeceras de las respuestas y a los datos persistentes del usuario. Se utilizan muy a menudo en la capa del controlador.
  • Los filtros son trozos de código ejecutados para cada petición, antes o después de una acción. Por ejemplo, los filtros de seguridad y validación son comúnmente utilizados en aplicaciones web. Puedes extender el framework creando tus propios filtros.

El Controlador Frontal

Todas las peticiones web son manejadas por un solo controlador frontal, que es el punto de entrada único de toda la aplicación en un entorno determinado. Cuando el controlador frontal recibe una petición, utiliza el sistema de enrutamiento para asociar el nombre de una acción y el nombre de un módulo con la URL escrita (o pinchada) por el usuario.

Si no estás interesado en los mecanismos internos de Symfony, eso es todo que necesitas saber sobre el controlador frontal. Es un componente imprescindible de la arquitectura MVC de Symfony, pero raramente necesitarás cambiarlo.

El Trabajo del Controlador Frontal en Detalle

El controlador frontal se encarga de despachar las peticiones, lo que implica algo más que detectar la acción que se ejecuta. De hecho, ejecuta el código común a todas las acciones, incluyendo:

  1. Carga la clase de configuración del proyecto y las librerías de Symfony.
  2. Crea la configuración de la aplicación y el contexto de Symfony.
  3. Carga e inicializa las clases del núcleo del framework.
  4. Carga la configuración.
  5. Decodifica la URL de la petición para determinar la acción a ejecutar y los parámetros de la petición.
  6. Si la acción no existe, redireccionará a la acción del error 404.
  7. Activa los filtros (por ejemplo, si la petición necesita autenticación).
  8. Ejecuta los filtros, primera pasada.
  9. Ejecuta la acción y produce la vista.
  10. Ejecuta los filtros, segunda pasada.
  11. Muestra la respuesta.
El Controlador Frontal por defecto

El controlador frontal por defecto, llamado index.php y ubicado en el directorio web/ del proyecto, es un simple script,

<?php
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod',
false);
sfContext::createInstance($configuration)->dispatch();

El controlador frontal incluye la configuración de la aplicación. La llamada al método dispatch() del objeto sfController (que es el controlador principal de la arquitectura MVC de Symfony) despacha la petición

Acciones

Las acciones son el corazón de la aplicación, puesto que contienen toda la lógica de la aplicación. Las acciones utilizan el modelo y definen variables para la vista. Cuando se realiza una petición web en una aplicación Symfony, la URL define una acción y los parámetros de la petición.

La clase de la acción

Las acciones son métodos con el nombre executeNombreAccion de una clase llamada nombreModuloActions que hereda de la clase sfActions y se encuentran agrupadas por módulos. La clase que representa las acciones de un módulo se encuentra en el archivo actions.class.php, en el directorio actions/ del módulo. Se muestra un ejemplo de un archivo actions.class.php con una única acción index para todo el módulo mimodulo. Ejemplo de la clase de la acción

class mimoduloActions extends sfActions
{
public function executeIndex()
{
// ...
}
}
Obteniendo Información en las Acciones

Las clases de las acciones ofrecen un método para acceder a la información relacionada con el controlador y los objetos del núcleo de Symfony. Se muestra como utilizarlos.

class mimoduloActions extends sfActions
{
public function executeIndex($peticion)
{
// Obteniendo parámetros de la petición
$password = $peticion->getParameter('password');
// Obteniendo información del controlador
$nombreModulo = $this->getModuleName();
$nombreAccion = $this->getActionName();
// Obteniendo objetos del núcleo del framework
$sesionUsuario = $this->getUser();
$respuesta = $this->getResponse();
$controlador = $this->getController();
$contexto = $this->getContext();
// Creando variables de la acción para pasar información a la plantilla
$this->setVar('parametro', 'valor');
$this->parametro = 'valor'; // Versión corta.
}
}
El "singleton" del contexto

En el controlador frontal ya se ha visto una llamada a sfContext::createInstance(). En una acción, el método getContext() devuelve el mismo singleton. Se trata de un objeto muy útil que guarda una referencia a todos los objetos del núcleo de Symfony relacionados con una petición dada, y ofrece un método accesor para cada uno de ellos:

  • sfController: El objeto controlador (->getController())
  • sfRequest: El objeto de la petición (->getRequest())
  • sfResponse: El objeto de la respuesta (->getResponse())
  • sfUser: El objeto de la sesión del usuario (->getUser())
  • sfDatabaseConnection: La conexión a la base de datos (->getDatabaseConnection())
  • sfLogger: El objeto para los logs (->getLogger())
  • sfI18N: El objeto de internacionalización (->getI18N())

Se puede llamar al singleton sfContext::getInstance() desde cualquier parte del código.

Terminación de las Acciones

Existen varias alternativas posibles cuando se termina la ejecución de una acción. El valor retornado por el método de la acción determina como será producida la vista. Para especificar la plantilla que se utiliza al mostrar el resultado de la acción, se emplean las constantes de la clase sfView.

Si existe una vista por defecto que se debe llamar (este es el caso más común), la acción debería terminar de la siguiente manera:

return sfView::SUCCESS;

Symfony buscará entonces una plantilla llamada nombreAccionSuccess.php. Este comportamiento se ha definido como el comportamiento por defecto, por lo que si omites la sentencia return en el método de la acción, Symfony también buscará una plantilla llamada nombreAccionSuccess.php. Las acciones vacías también siguen este comportamiento.

Para utilizar una vista personalizada, se debe utilizar el siguiente valor de retorno:

return 'MiResultado';

Symfony entonces buscará una plantilla llamada nombreAccionMiResultado.php. Si no se utiliza ninguna vista (por ejemplo, en el caso de una acción ejecutada en un archivo de lotes) la acción debe terminar de la siguiente forma:

return sfView::NONE;

Sesiones de Usuario

Symfony maneja automáticamente las sesiones del usuario y es capaz de almacenar datos de forma persistente entre peticiones. Utiliza el mecanismo de manejo de sesiones incluido en PHP y lo mejora para hacerlo más configurable y más fácil de usar.

Accediendo a la Sesión de Usuario

El objeto sesión del usuario actual se accede en la acción con el método getUser(), que es una instancia de la clase sfUser. Esta clase dispone de un contenedor de parámetros que permite guardar cualquier atributo del usuario en el. Esta información estará disponible en otras peticiones hasta terminar la sesión del usuario, como se muestra en el Listado 6-15. Los atributos de usuarios pueden guardar cualquier tipo de información (cadenas de texto, arrays y arrays asociativos). Se pueden utilizar para cualquier usuario, incluso si ese usuario no se ha identificado.

class mimoduloActions extends sfActions
{
public function executePrimeraPagina($peticion)
{
$nombre = $peticion->getParameter('nombre');
// Guardar información en la sesión del usuario
$this->getUser()->setAttribute('nombre', $nombre);
}
public function executeSegundaPagina()
{
// Obtener información de la sesión del usuario con un valor por defecto
$nombre = $this->getUser()->getAttribute('nombre', 'Anónimo');
}
}

Cuidado Puedes guardar objetos en la sesión del usuario, pero no se recomienda hacerlo. El motivo es que el objeto de la sesión se serializa entre una petición y otra. Cuando la sesión se deserializa, la clase del objeto guardado debe haber sido previamente cargada y este no es siempre el caso. Además, puede haber objetos de tipo "stalled" si se guardan objetos de Propel.

Como muchos otros getters en Symfony, el método getAttribute() acepta un segundo parámetro, especificando el valor por defecto a ser utilizado cuando el atributo no está definido.

Para verificar si un atributo ha sido definido para un usuario, se utiliza el método hasAttribute(). Los atributos se guardan en un contenedor de parámetros que puede ser accedido por el método getAttributeHolder(). También permite un borrado rápido de los atributos del usuario con los métodos usuales del contenedor de parámetros, como se muestra en el en el siguiente ejemplo eliminando información de la sesión del usuario

class mimoduloActions extends sfActions
{
public function executeBorraNombre()
{
$this->getUser()->getAttributeHolder()->remove('nombre');
}
public function executeLimpia()
{
$this->getUser()->getAttributeHolder()->clear();
}
}

Los atributos de la sesión del usuario también están disponibles por defecto en las plantillas mediante la variable $sf_user, que almacena el objeto sfUser actual

Manejo de Sesiones

El manejo de sesiones de Symfony se encarga de gestionar automáticamente el almacenamiento de los IDs de sesión tanto en el cliente como en el servidor. Sin embargo, si se necesita modificar este comportamiento por defecto, es posible hacerlo. Se trata de algo que solamente lo necesitan los usuarios más avanzados.

En el lado del cliente, las sesiones son manejadas por cookies. La cookie de Symfony se llama Symfony, pero se puede cambiar su nombre editando el archivo de configuración factories.yml, C

Cambiando el nombre de la cookie de sesión, en apps/frontend/config/
factories.yml
all:
storage:
class: sfSessionStorage
param:
session_name: mi_nombre_cookie

La sesión se inicializa (con la función de PHP session_start()) solo si el parámetro auto_start de factories.yml tiene un valor de true (que es el caso por defecto). Si se quiere iniciar la sesión manualmente, se debe cambiar el valor de esa opción de configuración del archivo factories.yml.

El manejo de sesiones de Symfony esta basado en las sesiones de PHP. Por tanto, si la gestión de la sesión en la parte del cliente se quiere realizar mediante parámetros en la URL en lugar de cookies, se debe modificar el valor de la directiva use_trans_sid en el archivo de configuración php.ini. No obstante, se recomienda no utilizar esta técnica.

session.use_trans_sid = 1

En el lado del servidor, Symfony guarda por defecto las sesiones de usuario en archivos. Se pueden almacenar en la base de datos cambiando el valor del parámetro class en factories.yml.

all:
storage:
class: sfMySQLSessionStorage
param:
db_table: session # Nombre de la tabla que guarda las sesiones
database: propel # Nombre de la conexión a base de datos que se
utiliza
# Parámetros opcionales
db_id_col: sess_id # Nombre de la columna que guarda el
identificador de la sesión
db_data_col: sess_data # Nombre de la columna que guarda los datos de
la sesión
db_time_col: sess_time # Nombre de la columna que guarda el timestamp
de la sesión

La opción database define el nombre de la conexión a base de datos que se utiliza. Posteriormente, Symfony utiliza el archivo databases.yml para determinar los parámetros con los que realiza la conexión (host, nombre de la base de datos, usuario y password).

Las clases disponibles para el almacenamiento de sesiones son sfMySQLSessionStorage, sfMySQLiSessionStorage, sfPostgreSQLSessionStorage y sfPDOSessionStorage. La clase recomendada es sfPDOSessionStorage. Para deshabilitar completamente el almacenamiento de las sesiones, se puede utilizar la clase sfNoStorage.

La expiración de la sesión se produce automáticamente después de 30 minutos. El valor de esta opción se puede modificar para cada entorno en el mismo archivo de configuración factories.yml, concretamente en la factoría correspondiente al usuario (user), tal y como muestra

// Cambiando el tiempo de vida de la sesión, en apps/frontend/config/ factories.yml

all:
user:
class: myUser
param:
timeout: 1800 # Tiempo de vida de la sesión en segundos

Seguridad de la Acción

La posibilidad de ejecutar una acción puede ser restringida a usuarios con ciertos privilegios. Las herramientas proporcionadas por Symfony para este propósito permiten la creación de aplicaciones seguras, en las que los usuarios necesitan estar autenticados antes de acceder a alguna característica o a partes de la aplicación. Añadir esta seguridad a una aplicación requiere dos pasos: declarar los requerimientos de seguridad para cada acción y autenticar a los usuarios con privilegios para que puedan acceder estas acciones seguras.

Restricción de Acceso

Antes de ser ejecutada, cada acción pasa por un filtro especial que verifica si el usuario actual tiene privilegios de acceder a la acción requerida. En Symfony, los privilegios están compuestos por dos partes:

  • Las acciones seguras requieren que los usuarios estén autenticados.
  • Las credenciales son privilegios de seguridad agrupados bajo un nombre y que permiten organizar la seguridad en grupos.

Para restringir el acceso a una acción se crea y se edita un archivo de configuración YAML llamado security.yml en el directorio config/ del módulo. En este archivo, se pueden especificar los requerimientos de seguridad que los usuarios deberán satisfacer para cada acción o para todas (all) las acciones. SE muestra un ejemplo de security.yml.

Estableciendo restricciones de acceso, en apps/frontend/modules/mimodulo/ config/security.yml
ver:

is_secure: off # Todos los usuarios pueden ejecutar la acción "ver"
modificar:
is_secure: on # La acción "modificar" es sólo para usuarios autenticados
borrar:
is_secure: on # Sólo para usuarios autenticados
credentials: admin # Con credencial "admin"
all:
is_secure: off # off es el valor por defecto

Las acciones no incluyen restricciones de seguridad por defecto, así que cuando no existe el archivo security.yml o no se indica ninguna acción en ese archivo, todas las acciones son accesibles por todos los usuarios. Si existe un archivo security.yml, Syfmony busca por el nombre de la acción y si existe, verifica que se satisfagan los requerimientos de seguridad. Lo que sucede cuando un usuario trata de acceder una acción restringida depende de sus credenciales:

  • Si el usuario está autenticado y tiene las credenciales apropiadas, entonces la acción se ejecuta.
  • Si el usuario no está autenticado, es redireccionado a la acción de login.
  • Si el usuario está autenticado, pero no posee las credenciales apropiadas, será redirigido a la acción segura por defecto, como muestra la figura

Filtros

El mecanismo de seguridad puede ser entendido como un filtro, por el que debe pasar cada petición antes de ejecutar la acción. Según las comprobaciones realizadas en el filtro, se puede modificar el procesamiento de la petición ,por ejemplo, cambiando la acción ejecutada (default/secure en lugar de la acción solicitada en el caso del filtro de seguridad). Symfony extiende esta idea a clases de filtros. Se puede especificar cualquier número de clases de filtros a ser ejecutadas antes de que se procese la respuesta, y además hacerlo de forma sistemática para todas las peticiones. Se pueden entender los filtros como una forma de empaquetar cierto código de forma similar a preExecute() y postExecute(), pero a un nivel superior (para toda una aplicación en lugar de para todo un módulo).

La Vista

La vista se encarga de producir las páginas que se muestran como resultado de las acciones. La vista en Symfony está compuesta por diversas partes, estando cada una de ellas especialmente preparada para que pueda ser fácilmente modificable por la persona que normalmente trabaja con cada aspecto del diseño de las aplicaciones.

  • Los diseñadores web normalmente trabajan con las plantillas (que son la presentación de los datos de la acción que se está ejecutando) y con el layout (que contiene el código HTML común a todas las páginas). Estas partes están formadas por código HTML que contiene pequeños trozos de código PHP, que normalmente son llamadas a los diversos helpers disponibles.
  • Para mejorar la reutilización de código, los programadores suelen extraer trozos de las plantillas y los transforman en componentes y elementos parciales. De esta forma, el layout se modifica para definir zonas en las que se insertan componentes externos. Los diseñadores web también pueden trabajar fácilmente con estos trozos de plantillas.
  • Los programadores normalmente centran su trabajo relativo a la vista en los archivos de configuración YAML (que permiten establecer opciones para las propiedades de la respuesta y para otros elementos de la interfaz) y en el objeto respuesta. Cuando se trabaja con variables en las plantillas, deben considerarse los posibles riesgos de seguridad de XSS (cross-site scripting) por lo que es necesario conocer las técnicas de escape de los caracteres introducidos por los usuarios. Independientemente del tipo de trabajo, existen herramientas y utilidades para simplificar y acelerar el trabajo (normalmente tedioso) de presentar los resultados de las acciones.

Plantillas

En el ejemplo se muestra el código típico de una plantilla. Su contenido está formado por código HTML y algo de código PHP sencillo, normalmente llamadas a las variables definidas en la acción (mediante la instrucción $this->nombre_variable = 'valor';) y algunos helpers.

<h1>Bienvenido</h1>
<p>¡Hola de nuevo, <?php echo $nombre ?>!</p>
<ul>¿Qué es lo que quieres hacer?
<li><?php echo link_to('Leer los últimos artículos', 'articulo/leer') ?></li>
<li><?php echo link_to('Escribir un nuevo artículo', 'articulo/escribir') ?></li>
</ul>

Es recomendable utilizar la sintaxis alternativa de PHP en las plantillas para hacerlas más fáciles de leer a aquellos desarrolladores que desconocen PHP. Se debería minimizar en lo posible el uso de código PHP en las plantillas, ya que estos archivos son los que se utilizan para definir la interfaz de la aplicación, y muchas veces son diseñados y modificados por otros equipos de trabajo especializados en el diseño de la presentación y no de la lógica del programa. Además, incluir la lógica dentro de las acciones permite disponer de varias plantillas para una sola acción sin tener que duplicar el código.

Helpers

Los helpers son funciones de PHP que devuelven código HTML y que se utilizan en las plantillas. En el ejemplo, la función link_to() es un helper. A veces, los helpers solamente se utilizan para ahorrar tiempo, agrupando en una sola instrucción pequeños trozos de código utilizados habitualmente en las plantillas. Por ejemplo, es fácil imaginarse la definición de la función que representa a este helper:

<?php echo input_tag('nick') ?>
=> <input type="text" name="nick" id="nick" value="" />

function input_tag($name, $value = null)

En realidad, la función input_tag() que incluye Symfony es un poco más complicado que eso, ya que permite indicar un tercer parámetro que contiene otros atributos de la etiqueta <input>. Se puede consultar su sintaxis completa y sus opciones en la documentación de la API aquí.

La mayoría de las veces los helpers incluyen cierta inteligencia que evita escribir bastante código:

<?php echo auto_link_text('Por favor, visita nuestro sitio web www.ejemplo.com') ?>
=> Por favor, visita nuestro sitio web <a
href="http://www.ejemplo.com">www.ejemplo.com</a>

Los helpers facilitan la creación de las plantillas y producen el mejor código HTML posible en lo que se refiere al rendimiento y a la accesibilidad. Aunque se puede usar HTML normal y corriente, los helpers normalmente son más rápidos de escribir.

Quizás se pregunte por qué motivo los helpers se nombran con la sintaxis de los guiones bajos en vez de utilizar el método camelCase que se utiliza en el resto de Symfony. El motivo es que los helpers son funciones, y todas las funciones de PHP utilizan la sintaxis de los guiones bajos.

Helpers por defecto

En cada petición se cargan los grupos de helpers estándar (Partial, Cache y Form). Si se está seguro de que no se van a utilizar los helpers de algún grupo, se puede eliminar este grupo de la lista de helpers estándar, lo que evita que se tenga que procesar el archivo del helper en cada petición. En concreto, el grupo de helpers de formularios (Form) es bastante grande y por tanto, ralentiza la ejecución de las páginas que no utilizan formularios. Por tanto, es una buena idea modificar la opción standard_helpers del archivo settings.yml para no incluirlo por defecto:

all:
.settings:
standard_helpers: [Partial, Cache] # Se elimina "Form"

El único inconveniente es que todas las plantillas que utilicen formularios tienen que declarar explícitamente que utilizan los helpers del grupo Form mediante la instrucción use_helper('Form').

Configuración de la vista

  • La presentación HTML del resultado de la acción (que se guarda en la plantilla, en el layout y en los fragmentos de plantilla)
  • El resto, que incluye entre otros los siguientes elementos:
  • Declaraciones : palabras clave (keywords), descripción (description), duración de la caché, etc.
  • El título de la página: no solo es útil para los usuarios que tienen abiertas varias ventanas del navegador, sino que también es muy importante para que los buscadores indexen bien la página.
  • Inclusión de archivos: de JavaScript y de hojas de estilos.
  • Layout: algunas acciones necesitan un layout personalizado (ventanas emergentes, anuncios, etc.) o puede que no necesiten cargar ningún layout (por ejemplo en las acciones relacionadas con Ajax).

En la vista, todo lo que no es HTML se considera configuración de la propia vista y Symfony permite dos formas de manipular esa configuración. La forma habitual es mediante el archivo de configuración view.yml. Se utiliza cuando los valores de configuración no dependen del contexto o de alguna consulta a la base de datos. Cuando se trabaja con valores dinámicos que cambian con cada acción, se recurre al segundo método para establecer la configuración de la vista: añadir los atributos directamente en el objeto sfResponse durante la acción.

El archivo view.yml

Cada módulo contiene un archivo view.yml que define las opciones de su propia vista. De esta forma, es posible definir en un único archivo las opciones de la vista para todo el módulo entero y las opciones para cada vista. Las claves de primer nivel en el archivo view.yml son el nombre de cada módulo que se configura. Semuestra un ejemplo de configuración de la vista.

Ejemplo de archivo view.yml de módulo
editSuccess:
metas:
title: Edita tu perfil
editError:
metas:
title: Error en la edición del perfil
all:
stylesheets: [mi_estilo]
metas:
title: Mi sitio web
{
return '<input type="text" name="'.$name.'" id="'.$name.'" value="'.$value.'" />';
}

Se debe tener en cuenta que las claves principales del archivo view.yml son los nombres de las vistas, no los nombres de las acciones. Recuerda que el nombre de una vista se compone de un nombre de acción y un resultado de acción. Si por ejemplo la acción edit devuelve un valor igual a sfView::SUCCESS (o no devuelve nada, ya que este es el valor devuelto por defecto), el nombre de la vista sería editSuccess.

Las opciones por defecto para el módulo entero se definen bajo la clave all: en el archivo view.yml del módulo. Las opciones por defecto para todas las vistas de la aplicación se definen en el archivo view.yml de la aplicación. Una vez más, se tiene la configuración en cascada:

  • En apps/frontend/modules/mimodulo/config/view.yml, las definiciones de cada vista solo se aplican a una vista y además sus valores tienen preferencia sobre las opciones generales del módulo.
  • En apps/frontend/modules/mimodulo/config/view.yml, las definiciones bajo all: se aplican a todas las acciones del módulo y tienen preferencia sobre las definiciones de la aplicación.
  • En apps/frontend/config/view.yml, las definiciones bajo default: se aplican a todos los módulos y todas las acciones de la aplicación.

Por defecto no existen los archivos view.yml de cada módulo. Por tanto la primera vez que se necesita configurar una opción a nivel de módulo, se debe crear un nuevo archivo llamado view.yml en el directorio config/.

El Modelo

Hasta ahora, la mayor parte de los contenidos se ha dedicado a la construcción de páginas y al procesado de peticiones y respuestas. Sin embargo, la lógica de negocio de las aplicaciones web depende casi siempre en su modelo de datos. El componente que se encarga por defecto de gestionar el modelo en Symfony es una capa de tipo ORM (object/relational mapping) realizada mediante el proyecto Propel. En las aplicaciones Symfony, el acceso y la modificación de los datos almacenados en la base de datos se realiza mediante objetos; de esta forma nunca se accede de forma explícita a la base de datos. Este comportamiento permite un alto nivel de abstracción y permite una fácil portabilidad. Se va a explicar como crear el modelo de objetos de datos, y la forma en la que se acceden y modifican los datos mediante Propel. Además, se muestra la integración de Propel en Symfony.

¿Por qué utilizar un ORM y una capa de abstracción?

Las bases de datos son relacionales. PHP 5 y Symfony están orientados a objetos. Para acceder de forma efectiva a la base de datos desde un contexto orientado a objetos, es necesaria una interfaz que traduzca la lógica de los objetos a la lógica relacional. Como se explicó, esta interfaz se llama ORM (object-relational mapping) o "mapeo de objetos a bases de datos", y está formada por objetos que permiten acceder a los datos y que contienen en sí mismos el código necesario para hacerlo.

La principal ventaja que aporta el ORM es la reutilización, permitiendo llamar a los métodos de un objeto de datos desde varias partes de la aplicación e incluso desde diferentes aplicaciones.

La capa ORM también encapsula la lógica de los datos; como por ejemplo, el cálculo de la puntuación de un usuario de un foro en función de las aportaciones que ha realizado al foro y en función del éxito de esas aportaciones. Cuando una página quiere mostrar esa puntuación de un usuario, simplemente invoca un método del modelo de datos, sin preocuparse de cómo se realiza el cálculo. Si el método de cálculo sufre alguna variación, solo es necesario modificar el método que calcula la puntuación en el modelo, sin necesidad de modificar el resto de la aplicación.

La utilización de objetos en vez de registros y de clases en vez de tablas, tiene otra ventaja: permite añadir métodos accesores en los objetos que no tienen relación directa con una tabla. Si se dispone por ejemplo de una tabla llamada cliente con dos campos llamados nombre y apellidos, puede que se necesite un dato llamado NombreCompleto que incluya y combine el nombre y los apellidos. En el mundo orientado a objetos, es tan fácil como añadir un método accesor a la clase Cliente. Desde el punto de vista de la aplicación, no existen diferencias entre los atributos Nombre, Apellidos, NombreCompleto de la clase Cliente. Solo la propia clase es capaz de determinar si un atributo determinado se corresponde con una columna de la base de datos. Los métodos accesores en la clase del modelo permiten ocultar la estructura real de la tabla de la base de datos

public function getNombreCompleto()
{
return $this->getNombre().' '.$this->getApellidos();
}

Todo el código repetitivo de acceso a los datos y toda la lógica de negocio de los propios datos se puede almacenar en esos objetos. Imagina que se ha definido la clase CarritoCompra en la que se almacena Productos (que son objetos). Para obtener el precio total del carrito de la compra antes de realizar el pago, se puede crear un método que encapsula el proceso de cálculo, tal y como se muestra en el siguiente ejemplo: Los métodos accesores ocultan la lógica de los datos

public function getTotal()
{
$total = 0;
foreach ($this->getProductos() as $producto)
{
$total += $producto->getPrecio() * $producto->getCantidad();
}
return $total;
}

Existe otra consideración importante que hay que tener en cuenta cuando se crean elementos de acceso a los datos: las empresas que crean las bases de datos utilizan variantes diferentes del lenguaje SQL. Si se cambia a otro sistema gestor de bases de datos, es necesario reescribir parte de las consultas SQL que se definieron para el sistema anterior. Si se crean las consultas mediante una sintaxis independiente de la base de datos y un componente externo se encarga de traducirlas al lenguaje SQL concreto de la base de datos, se puede cambiar fácilmente de una base de datos a otra. Este es precisamente el objetivo de las capas de abstracción de bases de datos. Esta capa obliga a utilizar una sintaxis específica para las consultas y a cambio realiza el trabajo sucio de optimizar y adaptar el lenguaje SQL a la base de datos concreta que se está utilizando.

La principal ventaja de la capa de abstracción es la portabilidad, porque hace posible el cambiar la aplicación a otra base de datos, incluso en mitad del desarrollo de un proyecto. Si se debe desarrollar rápidamente un prototipo de una aplicación y el cliente no ha decidido todavía la base de datos que mejor se ajusta a sus necesidades, se puede construir la aplicación utilizando SQLite y cuando el cliente haya tomado la decisión, cambiar fácilmente a MySQL, PostgreSQL o Oracle. Solamente es necesario cambiar una línea en un archivo de configuración y todo funciona correctamente.

Symfony utiliza Propel como ORM y Propel utiliza PDO (PHP Data Objects) como capa de abstracción de bases de datos. Estos dos componentes externos han sido desarrollados por el equipo de Propel, y están completamente integrados en Symfony, por lo que se pueden considerar una parte más del framework. Su sintaxis y sus convenciones, se han adaptado de forma que difieran lo menos posible de las de Symfony.

Consultas en Propel

Cuando se utiliza un método de una clase ArticuloPeer para obtener los objetos, el resultado de la consulta pasa el proceso de "hidratación" ("hydrating" en inglés) en el que se crean los objetos y se cargan con los datos de las filas devueltas en el resultado de la consulta. Para obtener por ejemplo todas las filas de la tabla articulo mediante Propel, se ejecuta la siguiente instrucción:

$articulos = ArticuloPeer::doSelect(new Criteria());

La variable $articulos resultante es un array con los objetos de tipo ArticuloPeer. Cada objeto se crea e inicializa, lo que requiere cierta cantidad de tiempo. La consecuencia de este comportamiento es que, al contrario de lo que sucede con las consultas a la base de datos, la velocidad de ejecución de una consulta Propel es directamente proporcional al número de resultados que devuelve. De esta forma, los métodos del modelo deberían optimizarse para devolver solamente un número limitado de resultados. Si no se necesitan todos los resultados devueltos por Criteria, se deberían limitar mediante los métodos setLimit() y setOffset(). Si solamente se necesitan por ejemplo las filas de datos de la 10 a la 20 para una consulta determinada, se puede refinar el objeto Criteria como se muestra:

$c = new Criteria();
$c->setOffset(10); // Posición de la primera fila que se obtiene
$c->setLimit(10); // Número de filas devueltas
$articulos = ArticuloPeer::doSelect($c);

El código anterior se puede automatizar utilizando un paginador. El objeto sfPropelPager gestiona de forma automática los valores offset y limit para una consulta Propel, de forma que solamente se crean los objetos mostrados en cada página.

Uso de la caché

Una de las técnicas disponibles para mejorar el rendimiento de una aplicación consiste en almacenar trozos de código HTML o incluso páginas enteras para poder servirlas en futuras peticiones. Esta técnica se denomina "utilizar cachés" y se pueden definir tanto en el lado del servidor como en el del cliente.

Symfony incluye un sistema de caché en el servidor muy flexible. Con este sistema es muy sencillo guardar en un archivo una página entera, el resultado de una acción, un elemento parcial o un trozo de plantilla. La configuración del sistema de caché se realiza de forma intuitiva mediante archivos de tipo YAML. Cuando los datos se modifican, se pueden borrar partes de la caché de forma selectiva mediante la línea de comandos o mediante algunos métodos especiales en las acciones. Symfony también permite controlar la caché en el lado del cliente mediante las cabeceras de HTTP 1.1. En este capítulo se presentan todas estas técnicas y se dan pistas para determinar las mejoras que las cachés confieren a las aplicaciones.

Guardando la respuesta en la caché

El principio básico de las cachés de HTML es muy sencillo: parte o todo el código HTML que se envía al usuario como respuesta a su petición se puede reutilizar en peticiones similares. El código HTML se almacena en un directorio especial (el directorio cache/) donde el controlador frontal lo busca antes de ejecutar la acción. Si se encuentra el código en la caché, se envía sin ejecutar la acción, por lo que se consigue un gran ahorro de tiempo de ejecución. Si no se encuentra el código, se ejecuta la acción y su respuesta (la vista) se guarda en el directorio de la caché para las futuras peticiones.

Como todas las páginas pueden contener información dinámica, la caché HTML está deshabilitada por defecto. El administrador del sitio web debe activarla para mejorar el rendimiento de la aplicación. Symfony permite gestionar tres tipos diferentes de caché HTML:

  • Caché de una acción (con o sin layout)
  • Caché de un elemento parcial, de un componente o de un slot de componentes
  • Caché de un trozo de plantilla

Los dos primeros tipos de caché se controlan mediante archivos YAML de configuración. La caché de trozos de plantillas se controla mediante llamadas a helpers dentro de las propias plantillas.

El sistema de caché permite mejorar el rendimiento de la aplicación de forma variable en función del tipo de caché utilizado. La siguiente lista muestra los tipos de caché disponibles en Symfony ordenados de mayor a menor mejora en el rendimiento de la aplicación:

  • Super caché
  • Caché de una acción con layout
  • Caché de una acción sin layout
  • Caché de fragmentos de plantillas

Además, tambien se pueden guardar en la caché los elementos parciales y los componentes. Si la modificación de los datos del modelo o de la sesión obliga a borrar la caché para mantener la coherencia de la información, se puede realizar un borrado muy selectivo para no penalizar el rendimiento, ya que es posible borrar solamente los elementos modificados manteniendo todos los demás.

Durante el desarrollo de una aplicación, se dan muchas situaciones en las que es necesario borrar la caché:

  • Cuando se crea una clase nueva: añadir la clase a un directorio para el que funciona la carga automática de clases (cualquier directorio lib/ del proyecto) no es suficiente para que Symfony sea capaz de encontrarla en los entornos de ejecución que no sean el de desarrollo. En este caso, es preciso borrar la caché de la carga automática para que Symfony recorrer otra vez todos los directorios indicados en el archivo autoload.yml y pueda encontrar las nuevas clases.
  • Cuando se modifica la configuración en el entorno de producción: en producción, la configuración de la aplicación solamente se procesa durante la primera petición. Las siguientes peticiones utilizan la versión guardada en la cache. Por lo tanto, cualquier cambio en la configuración no tiene efecto en el entorno de producción (o en cualquier otro entorno donde la depuración de aplicaciones esté desactivada) hasta que se borre ese archivo de la caché.
  • Cuando se modifica una plantilla en un entorno en el que la caché de plantillas está activada: en producción siempre se utilizan las plantillas guardadas en la caché, por lo que todos los cambios introducidos en las plantillas se ignoran hasta que la plantilla guardada en la caché se borra o caduca.
  • Cuando se actualiza una aplicación mediante el comando project:deploy: este caso normalmente comprende las tres modificaciones descritas anteriormente.

El problema de borrar la caché entera es que la siguiente petición tarda bastante tiempo en ser procesada, porque se debe regenerar la caché de configuración. Además, también se borran de la caché las plantillas que no han sido modificadas por lo que se pierde la ventaja de haberlas guardado en la cache. Por este motivo, es una buena idea borrar de la caché solamente los archivos que hagan falta. Las opciones de la tarea cache:clear pueden definir un subconjunto de archivos a borrar de la caché, como muestra :

// Borrar sólo la caché de la aplicación "frontend"
> php symfony cache:clear frontend
// Borrar sólo la caché HTML de la aplicación "frontend"
> php symfony cache:clear frontend template
// Borrar sólo la caché de configuración de la aplicación "frontend"
> php symfony cache:clear frontend config

Una recomendación muy importante es la de probar cuidadosamente todas las páginas para las que se ha habilitado la caché, ya que suele ser habitual que se produzcan errores por haber guardado en la caché elementos inadecuados o por no haber borrado de la caché los elementos modificados. Una buena técnica es la de crear un entorno intermedio llamado staging dedicado a probar la caché y las mejoras en el rendimiento de la aplicación.

Por último, es posible exprimir al máximo algunas características del protocolo HTTP 1.1 gracias a las opciones que proporciona Symfony para controlar la caché y que permite aprovechar las ventajas de la caché en el navegador de los clientes, de forma que se aumente aún más el rendimiento de la aplicación.

Almacenamiento de la caché en una base de datos

Por defecto, los datos de la caché de plantillas se guardan en el sistema de archivos. Los trozos de HTML y los objetos serializados de la respuesta se guardan en el directorio cache/ del proyecto. Symfony también incluye un método de almacenamiento alternativo para la caché, la base de datos SQLite. Este tipo de base de datos consiste en un archivo simple que PHP es capaz de reconocer como base de datos y le permite buscar información en el archivo de forma muy eficiente. Para indicar a Symfony que debería utilizar el almacenamiento de SQLite en vez del sistema de archivos, se debe modificar la opción view_cache del archivo de configuración factories.yml:

view_cache:
class: sfSQLiteCache
param:
database: %SF_TEMPLATE_CACHE_DIR%/cache.db

La ventaja de utilizar el almacenamiento en SQLite es que la caché de las plantillas es mucho más fácil de leer y de escribir cuando el número de elementos de la caché es muy grande. Si la aplicación hace un uso intensivo de la caché, los archivos almacenados en ésta acaban en una estructura de directorios muy profunda, por lo que utilizar el almacenamiento de SQLite mejora el rendimiento de la aplicación.

Además, borrar una caché almacenada en el sistema de archivos requiere eliminar muchos archivos, por lo que es una operación que puede durar algunos segundos, durante los cuales la aplicación no está disponible. Si se utiliza el almacenamiento de SQLite, el proceso de borrado de la caché consiste en borrar un solo archivo, precisamente el archivo que se utiliza como base de datos SQLite. Independientemente del número de archivos en la caché, el borrado es instantáneo.

Características

Symfony, tiene muchas clases destinadas a tratar las funcionalidades más comunes y redundantes de los desarrollos. A continuación vamos a realizar una breve descripción de las automatizaciones de los elementos comunes de los proyectos web, como por ejemplo:

  • Facilita la internacionalización de los contenidos ya incluye clases que permiten la traducción de los datos y de la interfaz, así como la adaptación local de los contenidos.
  • Realiza una separación de la capa de presentación efectiva, manteniendo las plantillas y layouts que pueden desarrollarse de forma externa al framework e integrarse en el mismo. Se ofrecen una serie de helpers incluidos en el framework destinados a minimizar el código utilizado en la presentación, ya que encapsulan grandes bloques de código en llamadas simples a funciones.
  • Symfony valida los formularios así como permite el autorelleno de los mismos, facilitanto los mecanismo de seguridad asociados al formato de datos.
  • Se ofrecen mecanismos destinados a controlar las entradas de datos, permitiendo sanearlas y evitar ataques de datos corruptos.
  • Se ofrecen mecanismos para la gestión de caché.
  • Existen métodos destinados a controlar la autenticación y la gestión de credenciales facilitando la creación de secciones restringidas y la gestión de la seguridad de usuario.
  • El sistema de enrutamiento y las URL limpias permiten considerar a las direcciones de las páginas como parte de la interfaz, además de estar optimizadas para los buscadores.
  • Dado el uso de la paginación automatizada, el filtrado y la ordenación de datos son más fáciles de realizar.
  • Se simplifican las interacciones con Ajax mediante el uso de los helpers que permiten encapsular los efectos JavaScript compatibles con todos los navegadores en una única línea de código.

Contenidos relacionados