Caching Assetic Asset Collections

Recently I was working with Assetic for combining and minifying CSS and JS files but ran into an issue with regards to caching the created asset collection. The resulting minified file was being rewritten on every page load which was no good at all.

TL;DR: Get the asset collection's "last modified" property and compare it to the written minified file to determine if it needs updating.

Edit Feb 29, 2016: This post originally used String Assets to cache the collection's content. It was later found that the string asset caches would conflict due to string assets not having "source" or "target" paths which are a component of the hash. Though an arbitrary source could be set on a string asset to resolve the issue, it's probably better to just use the collection's getLastModified before writing the minified file. The post has been updated to reflect the new approach.

If you still want to cache a collection, store the contents in a string asset but be sure to at least set source of the string asset to something unique for the collection. This will ensure that if you have multiple collections generated on page load, they won't conflict.

// Build the string asset with a unique "source"
$collectionAsset = new StringAsset($collection->dump(), array(), 'css/' . $type);     // The $type here is the CSS media type (screen, print, etc) to make it unique

// Cache the string asset for later use
$cachedCollection = new AssetCache($collectionAsset, $cache);

Rundown of how to minify, combine, and cache assets with Assetic

  1. Loop through individual assets (CSS or JS files)
  2. Apply filters to minify them
  3. Cache the result so you don't have to minify them again until the source file changes
  4. Add the asset to an asset collection
  5. Finally, write the minified file if either of the following is true
    • The minified file does not exist
    • The asset collection's last modified is newer than that of the minified file on the system
      • This indicates that the minified file is now out of date from what's been cached
      • This was the key to ensure the minified file wasn't updated on every page load

The resulting file will be a combination of all minified assets which is only updated when one of the individual assets are updated.

The Code

The following assumes you have an array of file paths. It will write a minified file to the public directory of the site which is only later updated if any of the individual assets have changed.

use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Asset\AssetCache;
use Assetic\Cache\FilesystemCache;
use Assetic\Filter\CssMinFilter;

// Create the collection
$collection = new AssetCollection();

// Create the cache
$cache = new FilesystemCache('/some/path/to/store/cache');

// Loop through file paths
foreach ($files as $filePath) {
    // Create the asset
    $asset = new FileAsset($filePath);

    // Add the filter, for example CSS minification
    $asset->ensureFilter(new CssMinFilter());

    // Cache the asset so we don't have to reapply filters on future page loads
    $cachedAsset = new AssetCache($asset, $cache);

    // Add the cached asset to the collection

// Set where to save the file
$absolutePublicMinPath = "/absolute/public/path/here";

// If the minified file doesn't exist OR the minified file is out of date, write it to the file system
if (!file_exists($absolutePublicMinPath) || $collection->getLastModified() > filemtime($absolutePublicMinPath)) {
    file_put_contents($absolutePublicMinPath, $collection->dump());

There you go! You should now have a minified file which only updates when any of its individual assets are updated. Here's hoping this saves just one person some time and as always, let me know if there's a better approach out there! :)

Short URL: