The definitive guide of Symfony 1.2

12.2. Removing Items from the Cache

If the scripts or the data of your application change, the cache will contain outdated information. To avoid incoherence and bugs, you can remove parts of the cache in many different ways, according to your needs.

12.2.1. Clearing the Entire Cache

The cache:clear task of the symfony command line erases the cache (HTML, configuration, routing, and i18n cache). You can pass it arguments to erase only a subset of the cache, as shown in Listing 12-8. Remember to call it only from the root of a symfony project.

Listing 12-8 - Clearing the Cache

// Erase the whole cache
> php symfony cache:clear

// Short syntax
> php symfony cc

// Erase only the cache of the frontend application
> php symfony cache:clear  --app=frontend

// Erase only the HTML cache of the frontend application
> php symfony cache:clear --app=frontend --type=template

// Erase only the configuration cache of the frontend application
// The built-types are config, i18n, routing, and template.
> php symfony cache:clear --app=frontend --type=config

// Erase only the configuration cache of the frontend application and the prod environment
> php symfony cache:clear --app=frontend --type=config --env=prod

12.2.2. Clearing Selective Parts of the Cache

When the database is updated, the cache of the actions related to the modified data must be cleared. You could clear the whole cache, but that would be a waste for all the existing cached actions that are unrelated to the model change. This is where the remove() method of the sfViewCacheManager object applies. It expects an internal URI as argument (the same kind of argument you would provide to a link_to()), and removes the related action cache.

For instance, imagine that the update action of the user module modifies the columns of a User object. The cached versions of the list and show actions need to be cleared, or else the old versions, which contain erroneous data, are displayed. To handle this, use the remove() method, as shown in Listing 12-9.

Listing 12-9 - Clearing the Cache for a Given Action, in modules/user/actions/actions.class.php

public function executeUpdate($request)
{
  // Update a user
  $user_id = $request->getParameter('id');
  $user = UserPeer::retrieveByPk($user_id);
  $this->foward404Unless($user);
  $user->setName($request->getParameter('name'));
  ...
  $user->save();

  // Clear the cache for actions related to this user
  $cacheManager = $this->getContext()->getViewCacheManager();
  $cacheManager->remove('user/list');
  $cacheManager->remove('user/show?id='.$user_id);
  ...
}

Removing cached partials, components, and component slots is a little trickier. As you can pass them any type of parameter (including objects), it is almost impossible to identify their cached version afterwards. Let's focus on partials, as the explanation is the same for the other template components. Symfony identifies a cached partial with a special prefix (sf_cache_partial), the name of the module, and the name of the partial, plus a hash of all the parameters used to call it, as follows:

// A partial called by
<?php include_partial('user/my_partial', array('user' => $user) ?>

// Is identified in the cache as
@sf_cache_partial?module=user&action=_my_partial&sf_cache_key=bf41dd9c84d59f3574a5da244626dcc8

In theory, you could remove a cached partial with the remove() method if you knew the value of the parameters hash used to identify it, but this is very impracticable. Fortunately, if you add a sf_cache_key parameter to the include_partial() helper call, you can identify the partial in the cache with something that you know. As you can see in Listing 12-10, clearing a single cached partial — for instance, to clean up the cache from the partial based on a modified User — becomes easy.

Listing 12-10 - Clearing Partials from the Cache

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

// Is identified in the cache as
@sf_cache_partial?module=user&action=_my_partial&sf_cache_key=12

// Clear _my_partial for a specific user in the cache with
$cacheManager->remove('@sf_cache_partial?module=user&action=_my_partial&sf_cache_key='.$user->getId());

To clear template fragments, use the same remove() method. The key identifying the fragment in the cache is composed of the same sf_cache_partial prefix, the module name, the action name, and the sf_cache_key (the unique name of the cache fragment included by the cache() helper). Listing 12-11 shows an example.

Listing 12-11 - Clearing Template Fragments from the Cache

<!-- Cached code -->
<?php if (!cache('users')): ?>
  ... // Whatever
  <?php cache_save() ?>
<?php endif; ?>

// Is identified in the cache as
@sf_cache_partial?module=user&action=list&sf_cache_key=users

// Clear it with
$cacheManager->remove('@sf_cache_partial?module=user&action=list&sf_cache_key=users');

12.2.3. Clearing several cache parts at once (new in symfony 1.1)

The remove() method accepts keys with wildcards. It allows you to remove several cache parts with a single call. You can do for instance:

$cacheManager->remove('user/show?id=*');    // Remove for all user records

Another good example is with applications handling several languages, where the language code appears in all URLs. The URL to a user profile page should look like this:

http://www.myapp.com/en/user/show/id/12

To remove the cached profile of the user having an id of 12 in all languages, you can simply call:

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

This also works for partials:

$cacheManager->remove('@sf_cache_partial?module=user&action=_my_partial&sf_cache_key=*');    // Remove for all keys

The remove() method accepts two additional parameters, allowing you to define which hosts and vary headers you want to clear the cache for. This is because symfony keeps one cache version for each host and vary headers, so that two applications sharing the same code base but not the same hostname use different caches. This can be of great use, for instance, when an application interprets the subdomain as a request parameter (like http://php.askeet.com and http://life.askeet.com). If you don't set the last two parameters, symfony will remove the cache for the current host and for the all vary header. Alternatively, if you want to remove the cache for another host, call remove() as follows:

$cacheManager->remove('user/show?id=*');                     // Remove records for the current host and all users
$cacheManager->remove('user/show?id=*', 'life.askeet.com');  // Remove records for the host life.askeet.com and all users
$cacheManager->remove('user/show?id=*', '*');                // Remove records for every host and all users

The remove() method works in all the caching strategies that you can define in the factories.yml (not only sfFileCache, but also sfAPCCache, sfEAcceleratorCache, sfMemcacheCache, sfSQLiteCache, and sfXCacheCache).

12.2.4. Clearing cache across applications (new in symfony 1.1)

Clearing the cache across applications can be a problem. For instance, if an administrator modifies a record in the user table in a backend application, all the actions depending on this user in the frontend application need to be cleared from the cache. But the view cache manager available in the backend application doesn't know the frontend application routing rules (applications are isolated from each other). So you can't write this code in the backend:

$cacheManager = sfContext::getInstance()->getViewCacheManager(); // Retrieves the view cache manager of the backend
$cacheManager->remove('user/show?id=12');                        // The pattern is not found, since the template is cached in the frontend

The solution is to initialize a sfCache object by hand, with the same settings as the frontend cache manager. Fortunately, all cache classes in symfony provide a removePattern method providing the same service as the view cache manager's remove.

For instance, if the backend application needs to clear the cache of the user/show action in the frontend application for the user of id 12, it can use the following:

$frontend_cache_dir = sfConfig::get('sf_root_cache_dir').DIRECTORY_SEPARATOR.'frontend'.DIRECTORY_SEPARATOR.SF_ENV.DIRECTORY_SEPARATOR.'template';
$cache = new sfFileCache(array('cache_dir' => $frontend_cache_dir)); // Use the same settings as the ones defined in the frontend factories.yml
$cache->removePattern('user/show?id=12');

For different caching strategies, you just need to change the cache object initialization, but the cache removal process remains the same:

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