Autoload your javascript and css in CakePHP

Have you ever wanted to have all your css or javascripts files loaded automatically once they are on place under /webroot/js and webroot/css folders? Well this is the solution.

What is all about

I’ve build a quick helper which will include all files under the js/ and css/ directories. So once the javascripts and css are there, they will be loaded automatically.

How to use it?

Using it is really easy. It’s like a normal helper:
1. add the file autoload.php under your /app/views/helpers
2. add it in the $helpers var in the controller
3. add the following snippet in your layout file. It should be under /app/views/layouts:

<?php echo $autoload->javascript();?>

This will include all files under /webroot/js

<?php echo $autoload->css();?>

This will include all files under /webroot/css

<?php echo $autoload->all();?>

This will include all files under /webroot/js and /webroot/css

Key features:

1. The helper includes all files recursively.
2. The helper includes also files under /root/vendors/js and /root/app/vendors/js too (same apply to css). Vendors are loaded first. (Thanks to suggestion from Juan Basso)
3. The order of the files in one directory is based on the name, so now you can control the order as well. (Thanks to suggestion from Juan Basso)
4. Files starting with . (dot) are not included, files under directories starting with . are not included as well, so you can exclude some files
5. if you want to load some files after other use directories. For example if you want to include plugins after the main lib (jQuery), place them in plugins directory like:
js/plugins/plugin_one.js
js/plugins/plugin_another.js
js/.hidden/plugin_third.js
js/jquery.js
the result will be:

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/plugins/plugin_one.js"></script>
<script type="text/javascript" src="js/plugins/plugin_another.js"></script>

(same apply to css as well)

Warning: I’ve noticed that this helper doesn’t work properly with scriptaculous, because the script tries to load the additional plugins and they fail, but for jQuery there is not a problem at all.

Finally here is the code of the helper:

<?php
/**
 * Class which will include all files under webroot/js or webroot/css directories.
 * The process is recursive, because this way it will detect which should be loaded first.
 * Example:
 * under js files are
 * /webroot/js/jquery.js
 * /webroot/js/plugins/some_plugin.js
 * /webroot/js/plugins/another_plugin.js
 * /webroot/js/plugins/sependand_from_some_plugin/plugin_dependent.js
 *
 * The result should be
 * <script type="text/javascript" src="/js/jquery.js"/>
 * <script type="text/javascript" src="/js/plugins/some_plugin.js"/>
 * <script type="text/javascript" src="/js/plugins/another_plugin.js"/>
 * <script type="text/javascript" src="/js/plugins/sependand_from_some_plugin/plugin_dependent.js"/> etc.
 *
 * files and directories with prefix underscore will be skipped from inclusion.
 *
 * Usage:
 * include it in the <head> section in your layout with:
 * <?php echo $autoload->javascript();?> or
 * <?php echo $autoload->css();?>
 * <?php echo $autoload->all();?>
 *
 * include it in your var $helpers = array('Autoload')
 */

class AutoloadHelper extends AppHelper {
    //Used helpers
    var $helpers = array('Javascript', 'Html');
   
    //return all javascript collection
    function javascript(){
    //Get all files from Root VENDORS
    $files = $this->walker(VENDORS.DS.'js', null, 'js');
    $files = array_merge($files, $this->walker(APP.DS.'vendors'.DS.'js', null, 'js'));
    $files = array_merge($files, $this->walker(JS, null, 'js'));
    $files = array_unique($files);
    $files = $this->strip_slash($files);
    $collection = array();
    foreach($files as $file){
        $collection[] = $this->Javascript->link($file);
    }
    return implode("", $collection);
    }
   
    //Return all css collection
    function css(){
    //Get all files required
    $files = $this->walker(VENDORS.DS.'css', null, 'css');
    $files = array_merge($files, $this->walker(APP.DS.'vendors'.DS.'css', null, 'css'));
    $files = array_merge($files, $this->walker(CSS, null, 'css'));
    $files = array_unique($files);
    $collection = array();
    foreach($files as $file){
        $collection[] = $this->Html->css($file);
    }
    return implode("", $collection);
    }
    //Return both js and css collections
    function all(){
    $collection = array($this->javascript(), $this->css());
    return implode($collection);
    }
   
    //Remove first slash from the relative urls, because Javascript
    private function strip_slash($array){
    foreach($array as $key=>$value){
        if(substr($value, 0, 1) == '/'){
        $array[$key] = substr($value, 1);
        }
    }
    return $array;
    }
    // walks recursivelly on all files under specified directory.
    // return list for inclusion
    private function walker( $from, $web_dir = null, $extension){
    $d = dir($from);
    $dirs = array();
    $files = array();
    //First loop is to add files into returned array
    while (false !== ($entry = $d->read())) {
        //Exclude some special entries
        if( $entry == '.' || $entry == '..' || substr($entry, 0, 1) == '.' || $entry == 'views'){continue;}
        if(is_dir($from.DS.$entry)){
        //sub directories
        $dirs[] = $entry;
        }
        //adding only files with specified extension
        if(is_file($from.DS.$entry) && (substr($entry, -1*strlen($extension)) == $extension)){
        //Small hack because the using / or some string, cause problem for loading properly the urls/
        if($web_dir == null){
            $files[] = $entry;
        } else {
            $files[] = $web_dir.DS.$entry;
        }
        }
    }
    $d->close();
    //Sort files by name
    sort($files);
    //this one is to walk through all directories underneath
    foreach($dirs as $entry){
        $files = array_merge($files, $this->walker( $from.DS.$entry, $web_dir.'/'.$entry, $extension));
    }
    return $files;
    }
}
?>

I hope it will be useful to someone 😉

11 thoughts on “Autoload your javascript and css in CakePHP

  1. Pingback: Free Premium Social Media WordPress Theme: WicketPixie » Wordpress themes marketplace

  2. Juan Basso

    It’s nice! But as suggestion, you can search js/css files in vendors path too. For exemple, for me, jQuery and plugins are 3th and I put this files in vendors files. In webroot/[js/css] have only my scripts.

  3. Nik Chankov Post author

    Well, it’s true vendor directory is used for this purpose /I never used it for js libs/, but in the optimal case the only visible part of the project should be /app/webroot/, so vendors should be not possible to be accessed. I would suggest to create a link from vendors to js/ and I will modify the helper to walk through it first.

    What do you think?

  4. Juan Basso

    The vendors libs are public too via Cake. For exemple, if your vendors is in /root (with priv of apache) and enter in site/js/lala.js, if no exists apache-www/…/js/lala.js, Cake will try search in vendors path, in this case, /root/vendors/js/lala.js. Understanding?
    It’s works for both vendors paths (app vendor and “all” vendors).

    Other suggestion is use linux style for order, for exemple, in scripts to shutdown, S01 is executed first, S02, … if no SXX in name, it go rand in last.

  5. Nik Chankov Post author

    Juan, thanks for clarification. I would definitely try this for vendors. I fact I didn’t know that this working this way, because I never used JS in vendors. 🙂

    I didn’t get your idea about the ordering. What do you mean?

  6. Juan Basso

    In linux, you can create scripts to execute in shutdown. If you have 10 scripts to be implemented, but one should be executed before the other, you put in front of the filename the term S (shutdown) followed by 2 numbers indicating the order. If it is not formatted in this way, will run at the end. For example, if you have the scripts S02jquery.plug jquery.subplug and S01jquery, the order will be: S01jquery, S02jquery.plug and finally jquery.subplug. Got it?

  7. Nik Chankov Post author

    I know the logic for Startup and Shutdown sequence in Unix, but I didn’t understood what did you meant with it. Now I got it. So your proposal is to have order by name of the script. I don’t think it will be hard task and I will definitely implement it. I don’t know how useful it will be, but at least it will help with the case of scriptaculous.

    Edit: Juan, I’ve updated the helper, so now it support vendors as well as order too. Thanks for the suggestions

  8. Jeet

    Hey, link on right top ‘subscribe’ image is incorrect. I was trying to subscribe to your posts by email. Now I will stick to google reader 🙂

  9. Pingback: Fix Plugin_one.js Errors - Windows XP, Vista, 7 & 8

Leave a Reply

Your email address will not be published. Required fields are marked *