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!

Continue Reading

16 Dec

Inline Attachment 1.0

Github recently added a new feature in which you can add images inside a ordinary textarea by simply dragging an image inside it, or pasting (only in chrome) an image. While i was working on the markdown editor for Socialog i needed a similar way to add images to a blog post. Therefore i wrote a javascript plugin which does the exact same thing. The result is a plugin which can be used with jQuery, CodeMirror or standalone, so you can easily use it in your project.

During this project i've gained experience with Grunt.js, structuring a jQuery plugin and the HTML 5 File API which i will briefly describe.

While writing out some quick requirements for the plugin i quickly noticed that there had to be a way to build my javascript files into 3 different versions. The standalone version should be compiled inside the jQuery and CodeMirror version without having to copy the code manually, and ideally there should also be a minified production version.

Meet Grunt.js, a JS command line build tool

Grunt.js has proven itself in the jQuery project and became my weapon of choice to combine and minify the required files.

Like any other build tool you need a script which tells it what to do, create a grunt.js in your project directory and setup some tasks. Here are the tasks which i use in my plugin, for simplicity i removed the codemirror version.

grunt.initConfig({

    // Grunt can read json files, here we load the package.json values inside the pkg property so it can be used inside the banner
    pkg: grunt.file.readJSON('package.json'),

    meta: {
        banner: '/*!  - v - ' +
            ' */'
    },

    // Concat action provided by UglifyJS, it automatically runs every child object
    concat: {
        normal: {
            src: ['', 'src/inline-attach.js' ],
            dest: 'dist/inline-attach.js'
        },
        jquery: {
            src: ['', 'src/inline-attach.js', 'src/jquery.inline-attach.js' ],
            dest: 'dist/jquery.inline-attach.js'
        }
    },

    // Minify action provided by UglifyJS, automatically runs every child object
    min: {
        normal: {
            src: [ '', 'dist/inline-attach.js' ],
            dest: 'dist/inline-attach.min.js',
            separator: ';'
        },
        jquery: {
            src: [ '', 'dist/jquery.inline-attach.js' ],
            dest: 'dist/jquery.inline-attach.min.js',
            separator: ';'
        }
    }
});

// Load the plugin that provides the "concat" and "min" tasks
grunt.loadNpmTasks('grunt-contrib-uglify');

// Register the default task
grunt.registerTask('default', ['concat', 'min']);

The config is defined first, it contains data like the pkg variable which loads package.json so it can be used in for example the banner. The meta.banner property contains the string which can be placed above every generated file. It can be called with the `` tag.

Secondly the tasks are loaded using grunt.loadNpmTasks('grunt-contrib-uglify')

And last the default task is defined when grunt is called without arguments, if you are familiar with Apache Ant then the equivelant is:

When everything is done you can run grunt and it will generate everything in the dist/ folder, that saves a lot of time!

jQuery plugin

Writing your code as a jQuery plugin saves you a lot of time when reusing the plugin and makes it easy for other developers to integrate your code into their own projects.

Setting up the structure is really easy and a tutorial can be found at the jQuery plugin tutorial

HTML 5 File API

Handling files with the latest HTML 5 File API provides you with a lot of new options to handle files, like handling files which are dragged from the desktop, listening to a paste event inside a textbox and showing a preview of the just pasted image, or resizing a picture before uploading it to save time and data.

The end result is an easy image attachment system in which you can paste or drop images inside a textbox and include an image without a WYSIWYG editor.

The project can be found at Github

Continue Reading

09 Nov

Razko.nl

Welcome on my new blog, here i will post my thoughts on web development, personal projects and code that i'm working on.

There isn't much to see right now as i am working on some upcoming features like the code sandbox, portfolio and Github integration.

Continue Reading