Managing CSS and JavaScript files within a Zend Framework App

February 4, 2010

Zend Framework

Managing CSS and JavaScript files within a Zend Framework App

When I’m creating a large external facing website or application, one of the biggest messes I used to end up making was in my CSS and JavaScript files.

As a developer/designer (jack of all trades, master of none, if you will) I tend to stay away from some of the more common developer solutions that take care of a lot of the design overhead (e.g., jQuery UI framework, Dojo, etc). Flexibility is paramount to good design — boxing yourself into a specific layout for every page is bound to produce a boring, forgettable website. Design is highly underrated to developers, I believe a good aesthetic sense is a skill worth maturing for anyone who develops web applications. After all, you can have the most beautiful underlying code, but if it’s presentation is anything short of beautiful it completely loses its “wow” factor.

Full design control usually means large, thousand plus line CSS files that equal serious pain when it comes to maintenance or building upon. If you don’t namespace your selectors carefully you’ll end up paying for it down the road. And heaven forbid you apply a default HTML tag styling. So, ranting aside, how do you maintain flexibility and still keep tidy CSS and JavaScript? My solution is to keep the very same directory/file structure that is used for the application for my CSS and JavaScript. The best part is by writing a very simple view helper, it’s super easy to automate the proper head link and script file inclusions.

Here’s what my CSS and JavaScript view helpers look like:

File: application/views/helpers/JavascriptHelper.php

<?php 
class Zend_View_Helper_JavascriptHelper extends Zend_View_Helper_Abstract
{   
    function javascriptHelper() {
        $request = Zend_Controller_Front::getInstance()->getRequest();
        $file_uri = 'media/js/' . $request->getControllerName() . '/' . $request->getActionName() . '.js';

        if (file_exists($file_uri)) {
            $this->view->headScript()->appendFile('/' . $file_uri);
        }
    }
}

File: application/views/helpers/CssHelper.php

<?php  
class Zend_View_Helper_CssHelper extends Zend_View_Helper_Abstract 
{ 
    function cssHelper() { 
        $request = Zend_Controller_Front::getInstance()->getRequest(); 
        $file_uri = 'media/css/' . $request->getControllerName() . '/' . $request->getActionName() . '.css'; 
         
        if (file_exists($file_uri)) { 
            $this->view->headLink()->appendStylesheet('/' . $file_uri); 
        } 
         
        return $this->view->headLink(); 
         
    } 
}

With that done, add the helper to your layout in the section:

File: application/layouts/scripts/layout.phtml

<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
    <title>My app title</title> 
    <? $this->headLink()->appendStylesheet('/media/css/global.css') ?> 
    <? $this->headLink()->appendStylesheet('/media/css/iefix.css','screen','lt IE 7') ?>     
    <?= $this->cssHelper() ?>    
    <? $this->headScript()->appendFile('/media/js/jquery-1.3.2.min.js') ?> 
    <? $this->headScript()->appendFile('/media/js/global.js') ?> 
     
    <? $this->javascriptHelper() ?> 
    <?= $this->headScript() ?> 
</head>

Now, anytime a controller action is invoked it will look for a javascript and css file in the same controller/action file path. In the above examples I’ve hard-coded the CSS and Javascript parent directories (/public/media for me) directly in to the code, but this would be pretty easy if you wanted to throw it in a config file or something instead. Otherwise just change to your preferred directory and your good to go.

The next thing I’d like to explore is automatically packing/minifying all CSS and Javascript into one file (or possibly outputting them directly to the layout in-line) and then caching the results of that to optimize bandwidth usage. If I ever have the luxury of that being a priority :)


Andy Baird can be found blogging about his experiences with Zend Framework on his site, Leaking Abstraction.

8 Responses to “Managing CSS and JavaScript files within a Zend Framework App”

  1. jkulak Says:

    It seems like lot of files for user to download, but if you truncate your solution to append only controller css/js files, that might make sense for large websites!

    Cheers,
    Kuba

  2. gplocke Says:

    We have written something very similar to this technique. We only append the css and js files specific to each action for each request, and everything else (all the common things that are loaded on every page) is minified, gzipped, and cached using minify (http://code.google.com/p/minify/). This helps drastically reduce our load times.

    It’s nice to see that other people are using this technique for their css and js files.

  3. bensternthal Says:

    I offer up a different approach when CSS/JS files per controller is the exception not the rule.

    http://www.devpatch.com/2010/02/view-plugin/

    Thoughts and opinions are welcome.

    Best,
    Ben

  4. jasperken Says:

    Hello,

    I have been doing some research regarding this topic too. Personally I favor the use of ANT and build scripts above the php-side compression/generation.

    Using ANT build scripts you can concat and compress all your CSS (but only if required) this is a very good start: http://www.experts-exchange.com/articles/Web_Development/Miscellaneous/Publishing-you-websites-using-ANT-scripts.html

    Gr,
    Jasper

  5. playwithtoy Says:

    hey andy
    great post
    in theory this is exactly what i want
    but in practice im getting this error

    <b>Fatal error</b>: Uncaught exception ‘Zend_Loader_PluginLoader_Exception’ with message ‘Plugin by name ‘CssHelper’ was not found in the registry; used paths:
    Zend_View_Helper_: Zend/View/Helper/:./views/helpers/’ in /var/local/virtuals/www.acemetrix.com/library/Zend/Loader/PluginLoader.php:412
    Stack trace:
    #0 /var/local/virtuals/www.acemetrix.com/library/Zend/View/Abstract.php(1174): Zend_Loader_PluginLoader-&gt;load(‘CssHelper’)
    #1 /var/local/virtuals/www.acemetrix.com/library/Zend/View/Abstract.php(610): Zend_View_Abstract-&gt;_getPlugin(‘helper’, ‘cssHelper’)
    #2 /var/local/virtuals/www.acemetrix.com/library/Zend/View/Abstract.php(336): Zend_View_Abstract-&gt;getHelper(‘cssHelper’)
    #3 [internal function]: Zend_View_Abstract-&gt;__call(‘cssHelper’, Array)
    #4 /var/local/virtuals/www.acemetrix.com/application/layouts/scripts/default.phtml(7): Zend_View-&gt;cssHelper()
    #5 /var/local/virtuals/www.acemetrix.com/library/Zend/View.php(108): include(‘/var/local/virt…’)
    #6 /var/local/virtuals/www.acemetrix.com/library/Zend/View/Abstract.php(880): in <b>/var/local/virtuals/www.acemetrix.com/library/Zend/Loader/PluginLoader.php</b> on line <b>412</b><br />

    can you give me hand?

  6. fuhrysteve Says:

    Looks like you’re not loading your plugins.

    you probably need to have something like this in your application.ini:

    includePaths.forms = APPLICATION_PATH "/helpers"

  7. playwithtoy Says:

    hey steve
    thanks for posting
    so im assuming the statement you wrote above you realy meant
    includePaths.helpers = APPLICATION_PATH "/views/helpers"
    based upon the above example

    so heres a little more clarification on whats happening
    if i put the
    <?php echo $this->cssHelper(); ?> line
    in my view script, ie, views/scripts/home/index.phtml
    the plugin is found adn the script runs without error

    however that doesnt help me
    cuase i really want it in the layout as prescribed
    and thats when this fails with this error

    im not really familiar with the loading sequence of ZF
    so im thinking the plugins are loading after the layout
    and before the view???

    if it is – how can i fix this?

  8. hinesro Says:

    I appreciated your post, I ran across the same problem and ended up writing two drop-in helpers to manage it for me. You can see them at http://blog.hines57.com/2011/03/13/zendframework-minify/ – thanks again.