Wed 8 Aug 2007
A little while ago I wrote a post about how I manually combine/pack Javascript files. Even as I was writing it I was thinking “hey, this would be a great CakePHP plugin/component/helper. Before I could code it Brad Daily beat me to it. However there are a couple of things I didn’t like about his method, particularly the need for configuration files (how anti Cake!) and the need to run a console script to generated the packed versions.
My First Solution
Suitably inspired I whipped up my own version. And it was awesome. It was so friggen automagic that you didn’t even have to change your code. Just include the helper and it did the rest - assuming you were using the new (as in Cake 1.2) scripts_for_layout syntax and not adding your scripts inline.
The Snag
Unfortunately I had missed a small issue. Since the helper relied on the afterRender callback to do most of the work any js/css added in the layout was missed. See the afterRender is called after the view is rendered, but BEFORE the layout is rendered. Doh! Digging through the base helper I saw a promising callback, afterLayout. After a bit more digging I found that this is merely a placeholder (added sometime around new years) and isn’t actually called anywhere. I’m not sure if it would have worked in my case anyway, since I really needed something like duringLayoutHeaderRender callback.
My Second Solution
So I reworked the interface of the helper. It’s less automagic now, but works with scripts from both the layout and view. Basically instead of just echoing $scripts_for_layout you use the helper. Otherwise you still include JavaScript and CSS as normal, making sure to set inline to false. Full directions and an example are in the Sandbox as usual.
Pros and Cons
I like Brad’s helper better for certain projects. Particularly ones that uses a large amount of JavaScript, as his photo manager app does. But I think for most small sites, with just a few files the automatic approach works out better. Plus mine packs CSS files in addition to JS. This is probably a good time to mention that Brad’s app, SlideShowPro, is totally awesome. I recently used it when developing AndrewSaul.com. Basically I went from no photo gallery to full featured, cool looking, seamlessly integrated photo gallery in thirty minutes. Seriously, I was shocked when I read this was the product of just two guys working nights.
Bringing It Home
Checkout the helper. It’s kinda cool. The Sandbox jumped from a 64 to a 79 in YSlow after I applied it. I also included an .htaccess file that handles things like gzip, expire time and ETAGS. Unfortunately my host doesn’t support any of those, so I didn’t get any benefit from it.
Popularity: 83% [?]











August 21st, 2007 at 5:06 am
This is a nice little helper you’ve made! I also had a look at Brad’s implementation and wondered why he did it like that. A console script would make more sense for him since he releases a product which he has no control over once a user installs it on their server - therefore eliminating the need to make it dynamic.
November 30th, 2007 at 3:24 am
When you add lot of files, it gets crazy in Windows as it’s generating a very long cache filename. I suppose md5() would solve it.
December 7th, 2007 at 9:00 am
True the cache names can get long. I debated using an md5 to shorten them, but I liked maintaining some meaning in the filename. If it’s causing a problem for you, then you should definately implement it.
December 7th, 2007 at 9:02 am
Hey everyone,
The new Cake 1.2 release changes the way configuration is handled. So on line 51 or so change “if(DEBUG) {” to “if (Configure::read(’debug’)) {”
December 9th, 2007 at 1:43 pm
In the header section of the layout, I have the following:
http://bin.cakephp.org/view/1193049833
Unfortunately, the JS files are not merged, nor are they minified.
Any hints why ? (using a nightly build of Cake1.2 5818 2007-10-20 12:01:30Z)
December 9th, 2007 at 1:44 pm
echo googlelink is basically the following:
link(”http://maps.google.com/maps?file=api&v=2.87&key=ABQIAAAAekfK_HXex-q1Cusfgf6odhT2PGOO4vxN2giRORTFL_PjFK2ZqhQKfE6tErd4H2dAP_MHUb8lz0szUA”); ?>
still can’t figure out why it is not working !!
December 10th, 2007 at 6:40 am
Hey Seme,
A couple things to check.
1) Did you make the change I detailed in comment 4?
2) Is debug set to 0?
3) Try seperating the adding of the javascripts into individual lines instead of using the array() approach.
-Matt
December 10th, 2007 at 9:33 am
Hey Matt,
Thanks for the fast response.
Updating line 51 in the Asset.php was one of the first things I’ve done. Separating the addition of files did not make any difference.
However, changing debug from 2 or 1 to 0 yielded a blank page with only the page title displayed (body part is empty)!!!
I thought the problem might have to do with Google Maps, so I tried your method with a different page that doesn’t call or make use of the Google Maps API,
I placed the following lines in the Header section of the layout:
link(’jquery/jquery-1.1.4.pack.js’,false)); ?>
link(’jquery/jquery.lavalamp.js’,false)); ?>
link(’jquery/jqModal.js’,false)); ?>
codeBlock(’ var $j = jQuery.noConflict();’); ?>
link(array(’forms.js’,false)));?>
link(’prototype/prototype.js’,false));?>
link(’search.js’,false)); ?>
scripts_for_layout(); ?>
and unfortunately, no JS files were included in the header, and the page still is blank (debug is still set to 0)
And then, I tried the following:
link(’jquery/jquery-1.1.4.pack.js’,false)); ?>
link(’jquery/jquery.lavalamp.js’,false)); ?>
link(’jquery/jqModal.js’,false)); ?>
codeBlock(’ var $j = jQuery.noConflict();’); ?>
link(array(’forms.js’,false)));?>
link(’prototype/prototype.js’,false));?>
link(’search.js’,false)); ?>
scripts_for_layout(); ?>
and only var $j = jQuery.noConflict();’); was outputted in the header !!
When changing the code to what I originally had (before trying to make use of Asset.php):
link(’jquery/jquery-1.1.4.pack.js’)); ?>
link(’jquery/jquery.lavalamp.js’)); ?>
link(array(’jquery/jqModal.js’))); ?>
codeBlock(’ var $j = jQuery.noConflict();’); ?>
link((’forms.js’)));?>
link(’prototype/prototype.js’));?>
link(’search.js’)); ?>
scripts_for_layout(); ?>
The page worked perfectly fine, and everything was displayed correctly. This is how I left it for now. The JS files are not merged, nor minified, and I didn’t manage to get the helper to work.
December 10th, 2007 at 9:36 am
< ?php and echo statements were removed from my last post !! here is my original post:
http://bin.cakephp.org/view/671080668
December 10th, 2007 at 10:49 am
Seme,
The helper only does its thing when debug is 0, which can make it a bit tricky to debug. Try setting debug back to 2 and commenting out the return line on 52.
It sounds like you’re generating a PHP error, but not seeing the error with debug set at 0. I bet its a problem with one of the vendor files.
December 10th, 2007 at 12:01 pm
[...] been using this helper on some projects lately and ran into a few issues, plus there have been some good comments, so I figured it could use a few tweaks. The new version is available for download on the Sandbox [...]
December 26th, 2007 at 11:10 pm
I just created Asset Mapper using your Asset Packer as a base to build on top of.
http://marcgrabanski.com/code/asset-mapper/
January 18th, 2008 at 2:14 am
Glad to find md5() support (I already added it locally)
Some notes:
1. The JavaScript packer is not a packer but minifier
2. $tidy->settings['merge_selectors'] = false; needed to avoid messing up of CSS styles/rules
3. Doesn’t seem to handle codeblock
4. Some JavaScript such as sIFR needs separate script block, in that case this helper doesn’t seem to help. I’m thinking of adding prefix based configurations (e.g., x-foo.js (will exclude minify), s-foo.js (will minify and place in separate script block), p-foo.js (will pack instead of minify), etc)
5. I think, the invocation has to be placed in element and also has to be cached ‘coz it seems to call asset helper always
January 18th, 2008 at 9:11 am
Hey Rajesh,
Thanks for the comments. You’re right about the minifier/packer terms. When I was writing this I was trying to decide between JSmin and the PHP version of Dean Edwards Javascript Packer. I treated the words as interchangeable, but they clearly have different meanings.
Good tip on the merge_selectors. It’s probably not a great idea to use a feature that is described in their docs as “Very basic and has at least one bug. Hopefully there is a replacement soon.”
As for the codeblock thing…uh…I kind of figured this was a problem and be honest I don’t think I’ve ever used codeblock.
Can you explain #5 more - I’m not sure I follow. The helper is called every time to checks if it need to re-generate the files, but it doesn’t actually do the processing unless it needs to.
-Matt
January 19th, 2008 at 8:10 am
Minifier is the right choice and at least for me Dean packer’s packed scripts are freezing browsers.
Marc seems to have added codeBlock support; but I personally feel that his helper is overkill.
The major reason for choosing the Asset Helper is to speed up the site. But, the hit on the helper call is heavy–at least for me. Yes, I seems to be wrong on that using element with caching can help.
Over the top of my head, though Asset Helper is godsend, for the real benefit, one seems need to manually minify/pack.
January 20th, 2008 at 6:33 pm
[...] Rajesh Jeba Anbiah left a comment concerned about the performance hit when using the CakePHP Asset Packer Helper. The major reason [...]
January 20th, 2008 at 6:42 pm
Hey Rajesh,
Marc emailed when released his version. I keep meaning to check it out, but haven’t had the time. I did find the time to check out the performance hit of the helper…and write a post about it. I’m interested to know if the results I found are significantly different then what you’re seeing.
April 24th, 2008 at 4:19 pm
Matt,
Thank you so much for this. It helped a bunch. One thing, the version I got didn’t work unless I had it in debug mode 0. I needed to change a few lines of code to get it going. It may be my version of CakePHP but who knows. Anyways, here is the code..
$folder =& new Folder();
//make sure the cache folder exists
if (!file_exists($path . $this->cachePath) && $folder->create($path . $this->cachePath, “777″)) {
trigger_error(’Could not create ‘ . $path . $this->cachePath
. ‘. Please create it manually with 777 permissions’, E_USER_WARNING);
}
That should replace this,
$folder = new Folder;
//make sure the cache folder exists
if ($folder->create($path . $this->cachePath, “777″)) {
trigger_error(’Could not create ‘ . $path . $this->cachePath
. ‘. Please create it manually with 777 permissions’, E_USER_WARNING);
}
June 18th, 2008 at 4:57 pm
Just 2 lines of code that should be changed in the helper.
As of CakePHP 1.2 RC1, use of the vendor() function is deprecated. Because of this, the following lines need to be updated:
Replace:
vendor(’jsmin/jsmin’);
with:
App::import(’Vendor’, ‘JSMin’, array(’file’ => ‘jsmin’.DS.’jsmin-1.1.1.php’));
and Replace:
vendor(’csstidy/class.csstidy’);
With:
App::import(’Vendor’, ‘CSSTidy’, array(’file’ => ‘csstidy’.DS.’class.csstidy.php’));
This way, you don’t receive a warning on the top of every page