Roy van Kaathoven
Full Stack Developer

21 Jan

Managing assets in Zend Framework 2

When you are developing a module for Zend Framework 2 in combination with assets you will most likely run into the question on how to serve the files to your public folder.

There are quite a few methods to do this, i will explain the 3 most commonly used methods.

The examples assume that you are using the default module structure as demonstrated by the Zend Skeleton application, and that you are familiar with writing simple ZF 2 applications.

Default module structure

ModuleName/
    config/
        module.config.php
    public/
        css/
        images/
        js/
    src/
        ModuleName/
            <code>/
    view/
    Module.php

Method 1: Symlink

Creating symlinks is the most easy way to make sure that your files are available in the public folder, the downside is that you have to create these symlinks yourself and you have to manage them while deploying your application. Some IDE's also have trouble with the duplicate files that show up when you symlink them in your project.

Method 2: Rewriting

If you are using Apache then you can use AliasMatch to rewrite the requests to your module folders. Using the following rule which you can add to your Virtual Host configuration (this does not work in a .htaccess file) you can rewrite http://server/folder/module/file locally to /modulepath/modulename/public/folder/file.

AliasMatch /(css|js|images)/([^/]+)/(.*) /path/to/module/$2/public/$1/$3

While this method is very flexible and does not require any configuration when adding new modules it does have a few downsides. First it assumes that all the modules are placed in the same folder, which is not the case when you are using Composer. In the example only css, js and images are rewritten, when you need more folders then you have to add them manually. It also requires configuring your Virtual Host, something that may not be used when you are working on a dev environment.

Method 3: Asset Manager

My preferred way to serve files is using the AssetManager Module. It has a lot of functionality to filter, minify and optimize your files. It is based on Assetic which contains a lot more options.

What this module does is listen to requests that are being sent to the server, when the request is not being handled by a valid route or controller then it will look through the modules for a public file that matches the request. If it found any file then it will be served through the PHP request.

To get started you can install the module using Composer:

./composer.phar require rwoverdijk/assetmanager

After that you can configure your module to serve public files through AssetManager. First you need tell where the files can be found, to do this add the following configuration to your module.config.php:

 array(
        'resolver_configs' => array(
            'paths' => array(
                'module_name' => __DIR__ . '/../public/',
            )
        )
    )
);

Now AssetManager will look in the /module/public/ folder when it needs to find a file. You can verify if it works by requesting a file through the public folder of your application.

While this module makes it really easy to handle your assets it has the downside that files are being served through a PHP call. This creates a lot of overhead especially in a production environment and slows down the loading times of your website.

The module provides a few options to counter this problem by caching the files, or writing files to your public folder. After a request the caching layer will write the result of the file to the same location in your public folder so the second time it will not have to pass through PHP.

In order to do this you can add the following configuration which enables caching:

 array(
        'caching' => array(
            'default' => array(
                'cache'     => 'FilePath',
                'options' => array(
                    'dir' => 'public',
                )
            )
        )
    )
);

In this example i use default which means it caches every file that is being requested, if you want to specify the files that are being cached then replace default with the path to your asset.

To take this even further you can minify every javascript and css file that is being requested.

 array(
        'filters' => array(
            // Filter by MIME typ
            'application/javascript' => array(
                array(
                    'filter' => 'JSMin',
                ),
            ),
            // Filter by extension
            'js' => array(
                array(
                    'filter' => 'JSMin',
                ),
            ),
        ),
  ),
);

You have to provide the filter service yourself which is really to implement, in this example i am using JSMin

setContent(JSMin::minify($asset->getContent()));
    }
}

And then add the filter to the AssetManager configuration:

 array(
        'filters' => array(
            'application/javascript' => array(
                array(
                    'filter' => 'Application\Service\JSMinFilter', // FQCN
                ),
            ),
        ),
    ),
);

This is only a small demonstration of all the possibilities that the module has to offer, there are a lot of filters available in Assetic which optimize your images, compresses your files, parse SASS, LESS etc and many more.

Take a look at the available filters and read the wiki to get started with AssetManager in your project!