Minify helper for cakephp

This article is also available in the following languages:
By _k10_
A minify helper for js and css assets.
Lately, I have been working on a pretty large scale website involving cakephp. With all the feature requests in place, it was time for optimization. I usually follow Yahoo's Developer Network's best practices - http://developer.yahoo.com/performance/rules.html. It has got fantastic set of rules in there ( a must read for every web developer).

It is the same set of rules I implemented for my website - Property Jungle - http://www.propertyjungle.in.

As I didn't want re-invent any wheels, I zeroed down to project minify found here - http://code.google.com/p/minify/. It provides the following best practices out-of-box.

1. Minimise HTTP request - Since it unifies javascript (js) and css assets, only one call for each should be issued by a page
2. Adds an Expires or a Cache-control header: You don't want your server to issue the same js and css assets for every page load for a given user session.
3. Gzip components : Does that!
4. Minify js and css: Does that too!
5. Configure Etags: Phew, does that too!

Pretty good number of performance improvements. Ok enough of the introduction. Lets dive in.

Here are the sets to follow to get things working.

1. Dump the min folder from the minify source in your webroot directory - It should be accessible by http://server-name/min
2. Dump my minify helper into the helpers folder --> /views/helpers.

Helper Class:

<?php 
/***
 * Cakephp view helper to interface with http://code.google.com/p/minify/ project.
 * Minify: Combines, minifies, and caches JavaScript and CSS files on demand to speed up page loads.
 * @author: Ketan Shah - ketan.shah@gmail.com - http://www.innovatechnologies.in
 * Requirements: An entry in core.php - "MinifyAsset" - value of which is either set 'true' or 'false'. False would be usually set during development and/or debugging. True should be set in production mode.
 */

Class MinifyHelper extends AppHelper{
        
        var 
$helpers = array('Javascript','Html'); //used for seamless degradation when MinifyAsset is set to false;
        
        
function js($assets){
            if(
Configure::read('MinifyAsset')){
               
e(sprintf("<script type='text/javascript' src='%s'></script>",$this->_path($assets'js')));
            }
            else{
                
e($this->Javascript->link($assets));
            }
        }
        
        
        function 
css($assets){
            if(
Configure::read('MinifyAsset')){
                
e(sprintf("<link type='text/css' rel='stylesheet' href='%s' />",$this->_path($assets'css')));
            }
            else{
                
e($this->Html->css($assets));
            }
        }
        
        function 
_path($assets$ext){
            
$path $this->webroot "min/b=$ext&f=";
            foreach(
$assets as $asset){
                
$path .= ($asset ".$ext,");
            }
            return 
substr($path0count($path)-2);
        }
    }

?>

3. Add an entry 'Minify' in the $helpers array of the controller which will serve your page request.
4. Use $minify->js() and $minify->css() functions in the layout. Both functions require an array of asset names without their extensions.
5. Add an entry Configure::write('MinifyAsset',true) in your core.php file. Setting it false during debugging or development phase would be a good idea.

The helper code is pretty self-explanatory. It basically generates the list and script tags for css and js respectively with the 'src' attribute pointing to the 'min' folder in pre-specified format (when the 'MinifyAsset' is set to true in the core). Setting it false, fetches the assets from js and css folders.

Hope this helps you in getting up to speed quick and make a pretty noticeable performance improvement to your website. Any feedback or constructive criticism would be appreciated.

-Ketan.
http://www.innovatechnologies.in ketan _d0t_ shah _At_ innovatechnologies.in

Comments

  • Posted 02/19/11 11:06:52 PM
    Html->script($this->_path($url, 'js'), $options);
    }else{
    return $this->Html->script($url, $options);
    }
    }

    public function css($path, $rel = null, $options = array()){
    if(Configure::read('MinifyAsset') === true){
    return $this->Html->css($this->_path($path, 'css'), $rel, $options);
    }else{
    return $this->Html->css($path, $rel, $options);
    }
    }

    public function _path($assets, $ext){
    if(!is_array($assets)){
    $assets = array($assets);
    }

    $path = '/min-' . $ext . '?f=';

    foreach($assets as $asset){
    $path .= ($ext . '/' . $asset . '.' . $ext . ',');
    }

    return substr($path, 0, count($path) - 2);
    }
    }

    ?>
  • Posted 01/14/11 01:14:29 PM
    If anyone is interested, I've written an article about how to use YUI Loader with PHP Minify in CakePHP.
    http://7shifts.com/blog/using-php-minify-in-cakephp/
  • Posted 03/11/10 06:28:51 AM
    I tried almost everything to make it run in a themed view (app/views/themed/default/webroot/js/jquery.js) and it just WON'T go!

    any suggestions?
  • Posted 01/23/10 10:53:41 AM
    I've extended this helper with a few lines that you now can use as usual. See http://blog.meixner.at/2010/01/14/minify_js_css/ for further information
    Greetz
  • Posted 09/23/09 05:50:44 PM
    My layouts have the basic styles.css and jquery.js in them. Additional scripts or css needed is called from the specific view and passed in the view to the layout.

    VIEW: css('fancybox', null, array(), false); ?> link(array('jquery.easing.1.3' , 'jquery-ui-full-1.5.2.min'), false); ?>
    How can I use your idea with my setup?
  • Posted 05/11/09 10:55:15 AM
    On my application, I wanted to minify ALL my CSS including those with a media type of 'print.' Here's what I did to make this work:

    In the minify helper:

    function css($assets, $media='screen'){
          if(Configure::read('MinifyAsset')){
                e(sprintf("<link type='text/css' rel='stylesheet' href='%s' media='$media'/>",$this->_path($assets, 'css')));
          }
          else{
                e($this->Html->css($assets));
          }
    }

    And in the view, I added a new array for print stylesheets, passing in the 'print' param.

    $css_print_globals = array('common_print');
    $minify->css($css_print_globals, 'print');
  • Posted 05/06/09 04:57:25 PM
    i got your helper to *work* on my app but it seems for some reason that it is removing the ../ before url's in my css files...

    if i inspect with firebug and add the ../ back you the image appears. any ideas?

    i tried looking through the code for a place that removes them but no such luck

    apart from that it works cool

    BTW.. i have also tried the fixes posted by Claudinei and Lucas but that made things worse...
  • Posted 04/08/09 02:38:46 AM
    Hello EveryBody,

    I am facing a issue while deleteing a record from a table , the table doesn't have any 'id' feild present, can you please help me out so that instead of adding/editing my primary key I can del data using my default Pk which is 'fldAccessID'.

    Many Thanks,
    Rahul Sinhaa
    • Posted 04/09/09 02:55:47 AM
      Hello EveryBody,

      I am facing a issue while deleteing a record from a table , the table doesn't have any 'id' feild present, can you please help me out so that instead of adding/editing my primary key I can del data using my default Pk which is 'fldAccessID'.

      Many Thanks,
      Rahul Sinhaa

      This is *not* a forum. Try asking this on cake's mailing list/google group and someone will answer. This is a place to leave comments related to the article.
  • Posted 04/07/09 12:54:17 PM
    Hi there,
    I am very new to CakePHP and try to resolve the following error:
    Warning (2): Invalid argument supplied for foreach() [APP/views/helpers/minify.php, line 34]
    In my layout I replaced $html->css() with $minify->css()
    But then I get the error.

    You write:
    4. Use $minify->js() and $minify->css() functions in the layout. Both functions require an array of asset names without their extensions.

    How do I use the new function exactly? Do I need to add somehing more to my $minify->css()?

  • Posted 03/29/09 05:08:06 PM
    I've found an better way to fix the path problem.

    First, set the $min_documentRoot to $_SERVER['DOCUMENT_ROOT'].'app/webroot/' on min/config.php

    $min_documentRoot = $_SERVER['DOCUMENT_ROOT'].'app/webroot/';
    After that, edit the helper and change that assets loop to match the following one:
    foreach($assets as $asset){ 
        $path .= ("{$ext}/{$asset}.{$ext},");
    }

    there you go! at the view you just need to specify the file path after "app/webroot/js/" or "app/webroot/css/" without the file extension.

    I also prefer to change "js" and "css" functions to make use of the cake helper to print out script/css tag. This helps javascript's and css's tags to be printed out inside &lt;head&gt; even if called on the view and not only on the layout.

    function js($assets, $inline = false){
        if(Configure::read('MinifyAsset')){
            return $this->Javascript->link($this->_path($assets, 'js'), $inline);
        }else{
            return $this->Javascript->link($assets, $inline);
        }
    }


    function css($assets, $rel = null, $htmlAttributes = array(), $inline = false) {
        if(Configure::read('MinifyAsset')){
            return $this->Html->css($this->_path($assets, 'css'), $rel, $htmlAttributes, $inline);
        }else{
            return $this->Html->css($assets, $rel, $htmlAttributes, $inline);
        }
    }
  • Posted 02/03/09 06:21:42 PM
    Double comment, problem on the post... disregard
  • Posted 02/03/09 06:15:41 PM
    Just what you said was not enough for me. I'm using the latest release of minify (2.1.1).

    Got a lot of HTTP/1.0 400 Bad Request. It's seems to me that the minifier looks for a system file under the given path, it it only works if you give it a full file path such as /host/app/webroot/css, since it won't access http, therefore get .htaccess instructions.

    And I also found the b=$ext; part of the url to be buggy.

    So this is what I've done to your helper:


    function _path($assets, $ext){
                $path = $this->webroot . "min/f=";
                foreach($assets as $asset){
                    $path .= $this->webroot."app/webroot/".$ext."/";
                    $path .= ($asset . ".$ext,");
                }
                return substr($path, 0, count($path)-2);
            }

    It works well. Ugly though to have the full path on the final source, but it works.

    Thanks for your effort.
  • Posted 01/26/09 02:47:53 AM
    I was about to make this, but now that you did I can just drink coffee instead.

    Thanks :-)
  • Posted 01/18/09 07:37:32 AM
    Nice thing you brought up here. Thanks.

    If you call your functions js +css in the constructor or beforeRender-callback of the helper you can even leave out to call them in your view/layout. The inclusion in the helper-array would be enough to have it included.

    Optionally you could use the check of a constant to avoid double inclusion (if the helper is included more than once). Maybe the Html-/JavascriptHelper avoids that by default,don't know that for sure.
  • Posted 01/13/09 10:17:53 AM
    Hi, thanks for sharing! Can you please include here the source code to the helper instead of providing a link? Thanks!
    • Posted 01/17/09 12:28:01 AM
      thanks for reviewing. I have made the changes you asked for!
      _k10_

      Hi, thanks for sharing! Can you please include here the source code to the helper instead of providing a link? Thanks!

Comments are closed for articles over a year old