En Symfony, la vista está formada por dos partes:
<meta>: palabras clave (keywords), descripción (description), duración de la cache, etc. En la vista, todo lo que no es HTML se considera configuración de la propia vista y Symfony permite 2 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.
sfResponse y mediante el archivo view.yml, tiene preferencia el valor establecido mediante el objeto sfResponse.
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. El listado 7-16 muestra un ejemplo de configuración de la vista.
Listado 7-16 - 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 webview.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:
apps/miaplicacion/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.apps/miaplicacion/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.apps/miaplicacion/config/view.yml, las definiciones bajo default: se aplican a todos los módulos y todas las acciones de la aplicación.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/.
Después de ver la plantilla por defecto en el listado 7-5 y un ejemplo de la respuesta generada en el listado 7-6, puede que te preguntes dónde se definen las cabeceras de la página. En realidad, las cabeceras salen de las opciones de configuración por defecto definidas en el archivo view.yml de la aplicación que se muestra en el listado 7-17.
Listado 7-17 - Archivo de configuración de la vista de la aplicación, en apps/miaplicacion/config/view.yml
default:
http_metas:
content-type: text/html
metas:
title: symfony project
robots: index, follow
description: symfony project
keywords: symfony, project
language: en
stylesheets: [main]
javascripts: [ ]
has_layout: on
layout: layoutCada una de estas opciones se explica en detalle en la sección "Opciones de configuración de la vista".
Aunque el objeto response (objeto respuesta) es parte de la vista, normalmente se modifica en la acción. Las acciones acceden al objeto respuesta creado por Symfony, y llamado sfResponse, mediante el método getResponse(). El listado 7-18 muestra algunos de los métodos de sfResponse que se utilizan habitualmente en las acciones.
Listado 7-18 - Las acciones pueden acceder a los métodos del objeto sfResponse
class mimoduloActions extends sfActions { public function executeIndex() { $respuesta = $this->getResponse(); // Cabeceras HTTP $respuesta->setContentType('text/xml'); $respuesta->setHttpHeader('Content-Language', 'en'); $respuesta->setStatusCode(403); $respuesta->addVaryHttpHeader('Accept-Language'); $respuesta->addCacheControlHttpHeader('no-cache'); // Cookies $respuesta->setCookie($nombre, $contenido, $expiracion, $ruta, $dominio); // Atributos Meta y cabecera de la página $respuesta->addMeta('robots', 'NONE'); $respuesta->addMeta('keywords', 'palabra1 palabra2'); $respuesta->setTitle('Mi Página de Ejemplo'); $respuesta->addStyleSheet('mi_archivo_css'); $respuesta->addJavaScript('mi_archivo_javascript'); } }
Además de los métodos setter mostrados anteriormente para establecer el valor de las propiedades, la clase sfResponse también dispone de métodos getter que devuelven el valor de los atributos de la respuesta.
Los setters que establecen propiedades de las cabeceras de las páginas son uno de los puntos fuertes de Symfony. Como las cabeceras se envían lo más tarde posible (se envían en sfRenderingFilter) es posible modificar su valor todas las veces que sea necesario y tan tarde como haga falta. Además, incluyen atajos muy útiles. Por ejemplo, si no se indica el charset cuando se llama al método setContentType(), Symfony añade de forma automática el valor del charset definido en el archivo settings.yml.
$respuesta->setContentType('text/xml'); echo $respuesta->getContentType(); => 'text/xml; charset=utf-8'
Los códigos de estado de las respuestas creadas por Symfony siguen la especificación de HTTP. De esta forma, los errores devuelven un código de estado igual a 500, las páginas que no se encuentran devuelven un código 404, las páginas normales devuelven el código 200, las páginas que no han sido modificadas se reducen a una simple cabecera con el código 304 (en el Capítulo 12 se explica con detalle), etc. Este comportamiento por defecto se puede redefinir para establecer códigos de estado personalizados, utilizando el método setStatusCode() sobre la respuesta. Se puede especificar un código propio junto con un mensaje personalizado o solamente un código, en cuyo caso Symfony añade el mensaje más común para ese código.
$respuesta->setStatusCode(404, 'Esta página no existe');
content-language en vez de Content-Language cuando se utiliza el método setHttpHeader(), ya que Symfony se encarga de transformar el primer nombre indicado en el segundo nombre, que es el correcto.
Puede que hayas observador que existen 2 tipos diferentes de opciones para la configuración de la vista:
view.yml y el objeto respuesta utiliza un método set para ellas)view.yml utiliza arrays para almacenar los valores y el objeto respuesta utiliza métodos de tipo add)Hay que tener en cuenta por tanto que la configuración en cascada va sobreescribiendo los valores de las opciones de un solo valor y va añadiendo valores a las opciones que permiten valores múltiples. Este comportamiento se entiende mejor a medida que se avanza en este capítulo.
La información que almacenan las etiquetas <meta> de la respuesta no se muestra en el navegador, pero es muy útil para los buscadores. Además, permiten controlar la cache de cada página. Las etiquetas <meta> se pueden definir dentro de las claves http_metas: y metas: en el archivo view.yml, como se muestra en el listado 7-19, o utilizando los métodos addHttpMeta() y addMeta() del objeto respuesta dentro de la acción, como muestra el listado 7-20.
Listado 7-19 - Definir etiquetas <meta> en forma de clave: valor dentro del archivo view.yml
http_metas: cache-control: public metas: description: Página sobre economía en Francia keywords: economía, Francia
Listado 7-20 - Definir etiquetas <meta> como opciones de la respuesta dentro de la acción
$this->getResponse()->addHttpMeta('cache-control', 'public'); $this->getResponse()->addMeta('description', 'Página sobre economía en Francia'); $this->getResponse()->addMeta('keywords', 'economía, Francia');
Si se añade un nuevo valor a una clave que ya tenía establecido otro valor, se reemplaza el valor anterior por el nuevo valor establecido. Para las etiquetas <meta>, se puede añadir al método addHttpMeta() (y también a setHttpHeader()) un tercer parámetro con un valor de false para que añadan el valor indicado al valor que ya existía y así no lo reemplacen.
$this->getResponse()->addHttpMeta('accept-language', 'en'); $this->getResponse()->addHttpMeta('accept-language', 'fr', false); echo $this->getResponse()->getHttpHeader('accept-language'); => 'en, fr'
Para añadir las etiquetas <meta> en la página que se envía al usuario, se deben utilizar los helpers include_http_metas() y include_metas() dentro de la sección <head> (que es por ejemplo lo que hace el layout por defecto, como se vio en el listado 7-5). Symfony construye las etiquetas <meta> definitivas juntando de forma automática el valor de todas las opciones de todos los archivos view.yml (incluyendo el archivo por defecto mostrado en el listado 7-17) y el valor de todas las opciones establecidas mediante los métodos de la respuesta. Por tanto, el ejemplo del listado 7-19 acaba generando las etiquetas <meta> del listado 7-21.
Listado 7-21 - Etiquetas <meta> que se muestran en la página final generada
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="cache-control" content="public" /> <meta name="robots" content="index, follow" /> <meta name="description" content="FPágina sobre economía en Francia" /> <meta name="keywords" content="economía, Francia" />
Como característica adicional, la cabecera HTTP de la respuesta incluye el contenido establecido en http-metas aunque no se utilice el helper include_http_metas() en el layout o incluso cuando no se utiliza ningún layout. Por ejemplo, si se necesita enviar el contenido de una página como texto plano, se puede utilizar el siguiente archivo de configuración view.yml:
http_metas: content-type: text/plain has_layout: false
El título de las páginas web es un aspecto clave para los buscadores. Además, es algo muy cómodo para los navegadores modernos que incluyen la navegación con pestañas. En HTML, el título se define como una etiqueta y como parte de la metainformación de la página, así que en el archivo view.yml el título aparece como descendiente de la clave metas:. El listado 7-22 muestra la definición del título en el archivo view.yml y el listado 7-23 muestra la definición en la acción.
Listado 7-22 - Definición del título en view.yml
indexSuccess:
metas:
title: Los tres cerditosListado 7-23 - Definición del título en la acción (es posible crear títulos dinámicamente)
$this->getResponse()->setTitle(sprintf('Los %d cerditos', $numero));
En la sección <head> del documento final, se incluye la etiqueta <meta name="title"> sólo si se utiliza el helper include_metas(), y se incluye la etiqueta <title> sólo si se utiliza el helper include_title(). Si se utilizan los dos helpers (como se muestra en el layout por defecto del listado 7-5) el título aparece dos veces en el documento (como en el listado 7-6), algo que es completamente correcto.
Como se muestra en los listados 7-24 y 7-25, es muy sencillo añadir una hoja de estilos concreta o un archivo de JavaScript en la vista.
Listado 7-24 - Incluir un archivo en view.yml
indexSuccess: stylesheets: [miestilo1, miestilo2] javascripts: [miscript]
Listado 7-25 - Incluir un archivo en la acción
$this->getResponse()->addStylesheet('miestilo1'); $this->getResponse()->addStylesheet('miestilo2'); $this->getResponse()->addJavascript('miscript');
En cualquier caso, el argumento necesario es el nombre del archivo. Si la extensión del archivo es la que le corresponde normalmente (.css para las hojas de estilos y .js para los archivos de JavaScript) se puede omitir la extensión. Si el directorio donde se encuentran los archivos también es el habitual (/css/ para las hojas de estilos y /js/ para los archivos de JavaScript) también se puede omitir. Symfony es lo bastante inteligente como para añadir la ruta y la extensión correcta.
Al contrario que lo que sucede en la definición de los elementos meta y title, no es necesario utilizar ningún helper en las plantillas o en el layout para incluir estos archivos. Por tanto, la configuración mostrada en los listados anteriores genera el código HTML mostrado en el listado 7-26, independientemente del contenido de la plantilla o del layout.
Listado 7-26 - Resultado de incluir los archivos - No es necesario llamar a ningún helper en el layout
<head> ... <link rel="stylesheet" type="text/css" media="screen" href="/css/miestilo1.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/miestilo2.css" /> <script language="javascript" type="text/javascript" src="/js/miscript.js"> </script> </head>
sfCommonFilter. El filtro busca la etiqueta <head> de la respuesta y añade las etiquetas <link> y <script> justo antes de cerrar la cabecera con la etiqueta </head>. Por tanto, no se pueden incluir este tipo de archivos si no existe una etiqueta <head> en el layout o en las plantillas.
Recuerda que se sigue aplicando la configuración en cascada, por lo que cualquier archivo que se incluya desde el archivo view.yml de la aplicación se muestra en cualquier página de la aplicación. Los listados 7-27, 7-28 y 7-29 muestran este funcionamiento.
Listado 7-27 - Ejemplo de archivo view.yml de aplicación
default: stylesheets: [principal]
Listado 7-28 - Ejemplo de archivo view.yml de módulo
indexSuccess: stylesheets: [especial] all: stylesheets: [otra]
Listado 7-29 - Vista generada para la acción indexSuccess
<link rel="stylesheet" type="text/css" media="screen" href="/css/principal.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/otra.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/especial.css" />
Si no se quiere incluir un archivo definido en alguno de los niveles de configuración superiores, se puede añadir un signo - delante del nombre del archivo en la configuración de más bajo nivel, como se muestra en el listado 7-30.
Listado 7-30 - Ejemplo de archivo view.yml en el módulo y que evita incluir algunos de los archivos incluidos desde el nivel de configuración de la aplicación
indexSuccess: stylesheets: [-principal, especial] all: stylesheets: [otra]
Para eliminar todas las hojas de estilos o todos los archivos de JavaScript, se puede utilizar la siguiente sintaxis:
indexSuccess: stylesheets: [-*] javascripts: [-*]
Se puede ser todavía más preciso al incluir los archivos, ya que se puede utilizar un parámetro adicional para indicar la posición en la que se debe incluir el archivo (solo se puede indicar la posición primera o la última):
# En el archivo view.yml indexSuccess: stylesheets: [especial: { position: first }]
// En la acción $this->getResponse()->addStylesheet('especial', 'first');
Para modificar el atributo media de la hoja de estilos incluida, se pueden modificar las opciones por defecto de Symfony, como se muestra en los listados 7-31, 7-32 y 7-33.
Listado 7-31 - Definir el atributo media al añadir una hoja de estilos desde view.yml
indexSuccess: stylesheets: [principal, impresora: { media: print }]
Listado 7-32 - Definir el atributo media al añadir una hoja de estilos desde la acción
$this->getResponse()->addStylesheet('impresora', '', array('media' => 'print'));
Listado 7-33 - La vista que genera la configuración anterior
<link rel="stylesheet" type="text/css" media="print" href="/css/impresora.css" />
Dependiendo de la estructura gráfica del sitio web, pueden definirse varios layouts. Los sitios web clásicos tienen al menos dos layouts: el layout por defecto y el layout que muestran las ventanas emergentes.
Como se ha visto, el layout por defecto se define en miproyecto/apps/miaplicacion/templates/layout.php. Los layouts adicionales también se definen en el mismo directorio templates/. Para que una vista utilice un layout específico como por ejemplo miaplicacion/templates/mi_layout.php, se debe utilizar la sintaxis del listado 7-34 para los archivos view.yml o el listado 7-35 para definirlo en la acción.
Listado 7-34 - Definición del layout en view.yml
indexSuccess: layout: mi_layout
Listado 7-35 - Definición del layout en la acción
$this->setLayout('mi_layout');
Algunas vistas no requieren el uso de ningún layout (por ejemplo las páginas de texto y los canales RSS). En ese caso, se puede eliminar el uso del layout tal y como se muestra en los listados 7-36 y 7-37.
Listado 7-36 - Eliminar el layout en view.yml
indexSuccess: has_layout: false
Listado 7-37 - Eliminar el layout en la acción
$this->setLayout(false);
Capítulo original en inglés Chapter 7 - Inside The View Layer Traducido por Javier Eguíluz