Symfony 1.4, la guía definitiva

12.2. Eliminando elementos de la cache

Si se modifican los scripts o los datos de la aplicación, la información de la cache estará desfasada. Para evitar incoherencias y posibles errores, se pueden eliminar partes de la cache de varias formas en función de las necesidades de cada caso.

12.2.1. Borrando toda la cache

La tarea cache:clear del comando symfony se emplea para borrar la cache (la cache de HTML, de configuración, del sistema de enrutamiento y de la internacionalización). Para borrar solo una parte de la cache, se pueden pasar parámetros, tal y como se muestra en el listado 12-8. Este comando solo se puede ejecutar desde el directorio raíz del proyecto.

Listado 12-8 - Borrando la cache

// Borrar toda la cache
$ php symfony cache:clear

// Atajo para borrar toda la cache
$ php symfony cc

// Borrar sólo la cache de la aplicación frontend
$ php symfony cache:clear --app=frontend

// Borrar sólo la cache HTML de la aplicación frontend
$ php symfony cache:clear --app=frontend --type=template

// Borrar sólo la cache de configuración de la aplicación frontend
// Los valores permitidos de la opción type son: config, i18n, routing y template
$ php symfony cache:clear --app=frontend --type=config

// Borrar sólo la cache de configuración de la aplicación frontend en el entorno de producción
$ php symfony cache:clear --app=frontend --type=config --env=prod

12.2.2. Borrando partes de la cache

Cuando se modifican los datos de la base de datos, debería borrarse la cache de las acciones que tienen relación con los datos modificados. Aunque se podría borrar la cache entera, en este caso se borraría también la cache de todas las acciones que no tienen relación con los datos modificados. Por este motivo, Symfony proporciona el método remove() del objeto sfViewCacheManager. El argumento que se le pasa es una URI interna (tal y como se utilizan por ejemplo en la función link_to()) y se elimina la cache de la acción relacionada con esa URI.

Si se dispone de una acción llamada modificar en el módulo usuario, esta acción modifica el valor de los datos de los objetos Usuario. Las páginas de las acciones listado y ver de este módulo que se guardan en la cache deberían borrarse, ya que en otro caso, se mostrarían datos desfasados. Para borrar estas páginas de la cache, se utiliza el método remove() tal y como muestra el listado 12-9.

Listado 12-9 - Borrando la cache de una acción, en modules/usuario/actions/actions.class.php

public function executeModificar($request)
{
  // Modificar un usuario
  $id_usuario = $request->getParameter('id');
  $usuario = UsuarioPeer::retrieveByPk($id_usuario);
  $this->forward404Unless($usuario);
  $usuario->setNombre($peticion->getParameter('nombre'));
  ...
  $usuario->save();

  // Borrar la cache de las acciones relacionadas con este usuario
  $cacheManager = $this->getContext()->getViewCacheManager();
  $cacheManager->remove('usuario/listado');
  $cacheManager->remove('usuario/ver?id='.$id_usuario);
  ...
}

Eliminar de la cache los elementos parciales y los componentes es un poco más complicado. Como se les puede pasar cualquier tipo de parámetro (incluso objetos), es casi imposible identificar la versión guardada en la cache en cada caso. Como la explicación es idéntica para los tres tipos de elementos, solo se va a explicar el proceso para los elementos parciales. Symfony identifica los elementos parciales almacenados en la cache mediante un prefijo especial (sf_cache_partial), el nombre del módulo, el nombre del elemento parcial y una clave única o hash generada a partir de todos los parámetros utilizados en la llamada a la función:

// Un elemento parcial que se llama así
<?php include_partial('usuario/mi_parcial', array('user' => $user) ?>

// Se identifica en la cache de la siguiente manera
@sf_cache_partial?module=usuario&action=_mi_parcial&sf_cache_key=bf41dd9c84d59f3574a5da244626dcc8

En teoría, es posible eliminar un elemento parcial guardado en la cache mediante el método remove() siempre que se conozca el valor de todos los parámetros utilizados en ese elemento, aunque en la práctica es casi imposible conseguirlo. Afortunadamente, si se añade un parámetro denominado sf_cache_key en la llamada del helper include_partial(), se puede definir un identificador propio para ese elemento parcial. De esta forma, y como muestra el listado 12-10, es fácil borrar un elemento parcial (como por ejemplo borrar de la cache un elemento parcial que depende de un usuario que ha sido modificado):

Listado 12-10 - Borrando elementos parciales de la cache

<?php include_partial('usuario/mi_parcial', array(
  'user'         => $user,
  'sf_cache_key' => $user->getId()
) ?>

// Se identifica en la cache de la siguiente forma
@sf_cache_partial?module=usuario&action=_mi_parcial&sf_cache_key=12

// Se puede borrar la cache de _mi_parcial para un usuario específico
$cacheManager->remove('@sf_cache_partial?module=usuario&action=_mi_parcial&sf_cache_key='.$user->getId());

Este método no se puede utilizar para borrar todas las versiones de un elemento parcial guardadas en la cache. Más adelante, en la sección "Borrando la cache a mano" se detalla como conseguirlo.

El método remove() también se emplea para borrar fragmentos de plantillas. El nombre que identifica a cada fragmento en la cache se compone del perfijo sf_cache_partial, el nombre del módulo, el nombre de la acción y el valor de sf_cache_key (el identificador único utilizado en la llamada al helper cache()). El listado 12-11 muestra un ejemplo.

Listado 12-11 - Borrando fragmentos de plantilla en la cache

<!-- Código guardado en la cache -->
<?php if (!cache('usuarios')): ?>
  // Lo que sea...
  <?php cache_save() ?>
<?php endif; ?>

// Se identifica en la cache de la siguiente forma
@sf_cache_partial?module=usuario&action=listado&sf_cache_key=usuarios

// Se puede borrar con el siguiente método
$cacheManager->remove('@sf_cache_partial?module=usuario&action=listado&sf_cache_key=usuarios');

12.2.3. Borrando simultáneamente varias partes de la cache

El método remove() también acepta comodines en el nombre de las claves. De esta forma, es posible borrar varias partes de la cache en una única llamada:

// Borra de la cache las páginas de todos los usuarios
$cacheManager->remove('usuario/ver?id=*');

Otro ejemplo de uso de comodines es el de las aplicaciones disponibles en varios idiomas, donde el código del idioma aparece en todas las URL. En este caso, la URL de la página del perfil de un usuario será similar a la siguiente:

http://www.miaplicacion.com/en/usuario/ver/id/12

Para eliminar de la cache las páginas en cualquier idioma del usuario cuyo id es 12, se puede utilizar la siguiente instrucción:

$cache->remove('usuario/ver?sf_culture=*&id=12');

Lo anterior también funciona en los elementos parciales:

// Utiliza un comodín en el nombre de la clave para borrar todas las claves
$cacheManager->remove('@sf_cache_partial?module=usuario&action=_mi_parcial&sf_cache_key=*');

El método remove() acepta otros dos argumentos opcionales, que permiten definir las cabeceras host y vary para las que quieres borrar elementos de la cache. Symfony guarda en la cache una versión de la página para cada valor diferente de las cabeceras host y vary, por lo que si dos aplicaciones tienen el mismo código pero diferente hostname, las dos utilizan diferentes caches. La mayor utilidad de esta característica se da en las aplicaciones que interpretan los subdominios como parámetros de la petición (como php en la dirección http://php.askeet.com o life en http://life.askeet.com). Si no se indican los últimos dos parámetros, Symfony borra la cache para el host actual y para el valor all de la cabecera vary. A continuación se muestran ejemplos de cómo borrar la cache para diferentes host utilizando el método remove():

// Borra de la cache las páginas de todos los usuarios para el host actual
$cacheManager->remove('usuario/ver?id=*');
// Borra de la cache las páginas de todos los usuarios para el host life-askeet.com
$cacheManager->remove('usuario/ver?id=*', 'life.askeet.com');
// Borra de la cache las páginas de todos los usuarios para todos los hosts
$cacheManager->remove('usuario/ver?id=*', '*');

El método remove() funciona con todos los métodos de cache que se pueden definir en el archivo de configuración factories.yml (no sólo con sfFileCache sino también con sfAPCCache, sfEAcceleratorCache, sfMemcacheCache, sfSQLiteCache y sfXCacheCache).

12.2.4. Borrado de la cache de otras aplicaciones

El borrado de la cache de otras aplicaciones no una tarea sencilla. Imagina que un administrador modifica un registro en la tabla usuario de la aplicación backend. Tras la modificación, todas las acciones que dependen de ese usuario en la aplicación frontend deben ser borradas de la cache. Sin embargo, el gestor de la cache de la aplicación backend no conoce las reglas de enrutamiento de la aplicación frontend porque las aplicaciones se encuentran aisladas entre sí. Por lo tanto, no es posible utilizar código similar al siguiente:

// Primero se obtiene el gestor de la cache del backend
$cacheManager = sfContext::getInstance()->getViewCacheManager();

// El patrón no se encuentra porque la plantilla está en la cache del frontend
$cacheManager->remove('usuario/ver?id=12');

La solución consiste en inicializar manualmente un objeto de tipo sfCache con las mismas opciones que el gestor de la cache del frontend. Afortunadamente, todas las clases de la cache en Symfony incluyen un método llamado removePattern() con la misma funcionalidad que el método remove() del gestor de la cache.

Si por ejemplo la aplicación backend tiene que borrar la cache de la acción usuario/ver en la aplicación frontend para el usuario cuyo atributo id es 12, se puede utilizar la siguiente instrucción:

$directorio_cache_frontend = sfConfig::get('sf_cache_dir').DIRECTORY_SEPARATOR.'frontend'.DIRECTORY_SEPARATOR.sfConfig::get('sf_environment').DIRECTORY_SEPARATOR.'template';
// Utiliza las mismas opciones que el archivo factories.yml de la aplicación frontend
$cache = new sfFileCache(array('cache_dir' => $directorio_cache_frontend));
$cache->removePattern('usuario/ver?id=12');

Si utilizas otros mecanismos de cache, sólo es preciso cambiar la inicialización del objeto de la cache, ya que el proceso de borrado de la cache es idéntico:

$cache = new sfMemcacheCache(array('prefix' => 'frontend'));
$cache->removePattern('usuario/ver?id=12');