Quick Optimization of CakePHP Application with YSlow

Posted on 25/5/09 (2 comments)

I run a busy Slovenian site featuring weather radar image on Google maps - vreme.malamalca.com . It is a simple CakePHP app which collects weather and radar data, stores it in cache and outputs it in friendly format.

Recently my site is receiving some heavy traffic due to high probability of hail storms which are result of high temperatures - around 35° (95°F). Everyone wants to check if their car /or home/ is in immediate danger. Bad weather is causing high spikes of traffic and sluggish response times.

After checking my site with YSlow, it got "F" grade. My site needed some improvements. There are some steps I took which got my site to grade "B" of YSlow:

  1. I changed my cache engine to APC. This had a tremendous effect in responsiveness of my app (although it is not graded by YSlow)
  2. Next step was implementation of some Apache directives in .htacces in /webroot folder:

Here are new .htaccess contents:

    RewriteEngine On
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

	# compress content with type html, text, and css
	AddOutputFilterByType DEFLATE text/plain
	AddOutputFilterByType DEFLATE text/html
	AddOutputFilterByType DEFLATE text/xml
	AddOutputFilterByType DEFLATE text/css
	AddOutputFilterByType DEFLATE text/javascript
	AddOutputFilterByType DEFLATE application/xml
	AddOutputFilterByType DEFLATE application/xhtml+xml
	AddOutputFilterByType DEFLATE application/rss+xml
	AddOutputFilterByType DEFLATE application/javascript
	AddOutputFilterByType DEFLATE application/x-javascript

	BrowserMatch ^Mozilla/4 gzip-only-text/html
	BrowserMatch ^Mozilla/4\.0[678] no-gzip
	BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

	#AddOutputFilter DEFLATE js css
	# properly handle requests coming from behind proxies
	Header append Vary User-Agent

	ExpiresActive On
	ExpiresByType application/javascript "access plus 10 years"
	ExpiresByType text/css "access plus 10 years"
	ExpiresByType text/js "access plus 10 years"
	ExpiresByType text/javascript "access plus 10 years"
	ExpiresByType application/x-javascript "access plus 10 years"
	ExpiresByType image/png "access plus 10 years"
	ExpiresByType image/gif "access plus 10 years"
	ExpiresByType image/jpeg "access plus 10 years"

FileETag none

These adjustments made my site usable again even in spikes of 500 users per minute.

Note: some aditional warnings... this directives extend duration of cached items in your browser. You should know what are you trying to achieve.

Datepicker for CakePHP's splitted datetime controls

Posted on 28/12/08 (4 comments)

I hate CakePHP's datetime controls.

Having three (or even five with time) selectboxes for one single date is just to much. I wanted to simplify this process quite some time now. I also wanted a user friendly solution without changing my current code (with default controls). Javascript datepicker (popup)  would be the most common solution. Here's my implementation.

  1. Append a hidden field in every <div class="input date"> element
  2. This hidden field is converted into jQuery datepicker control
  3. Two sync routines are added - they take care of populating select elements with values picked from jQuery widget and vice versa

This is how it looks like. Click on calendar icon for javascript popup (it's an image...click on it for live demo page):

Here's the code:

<script type="text/javascript">
	$(document).ready(function() {
		$('div.date').append('<input class="datepicker" type="hidden"/>');
		$('.datepicker').datepicker({
			dateFormat: 'yy-mm-dd',
			buttonImage: '/uploads/calendar.png',
			buttonImageOnly: true,
			duration: '',
			showOn: 'button',
			onSelect: function(sel_date) {
				var newDate = sel_date.split('-');
				
				$(this).siblings('select').each(function(){
					id = $(this).attr('id');
					if (id.substring(id.length-3, id.length)=='Day') $(this).val(newDate[2]);
					else if (id.substring(id.length-5, id.length)=='Month') $(this).val(newDate[1]);
					else if (id.substring(id.length-4, id.length)=='Year') $(this).val(newDate[0]);
				});
			},
			beforeShow: function() {
				var year = '';
				var month = '';
				var day = '';
				var id = '';
				$(this).siblings('select').each(function() {
					id = $(this).attr('id');
					if (id.substring(id.length-3, id.length)=='Day') day = $(this).val();
					else if (id.substring(id.length-5, id.length)=='Month') month = $(this).val();
					else if (id.substring(id.length-4, id.length)=='Year') year = $(this).val();
				});
				$(this).val(year+'-'+month+'-'+day);
				return {};
			}
		});
	});
</script>

Edit: thanks Shubha for pointing out problem with IE. I've fixed it. Datepicker now works seamlesly in IE too.

LilFloat - localize your decimal input

Posted on 28/12/08 (0 comments)

LilFloat behavior takes care of your decimal input when your local numeric format differs from US standards.

Most of EU and other countries use comma for decimal separator and dot for thousands. Unfortunatelly CakePHP cannot handle this. Therefore I use this behavior. It automagicaly converts your decimals in a local representation into "default" version which is required for storing decimals into the database.

Wait! There is more. LilFloat behavior has also a validation method for your decimal input. Usage is as simple as adding a new validation rule. Did I mention that it can auto extract decimal places from your field type? Well, it does. Check tests for more samples.

<?php
class YourModel extends AppModel {
    var $name = 'YourModel';
    var $actsAs = array('LilFloat');   
    var $validate = array(
        'decimalfield' => array(
            'format' => array('rule' => 'isValidFloat'),
        ),
    );
}
?>

You can find LilFloat behaviour in my LilCake repository. Enjoy!

LilBlogs Wysiwyg editor

Posted on 26/8/08 in LilBlogs (2 comments)

I've finally implemented wysiwyg editor for LilBlogs. Currently text editor is still a default editor for posts but it will be replaced (as a default one) with one of the free html editors. For now, you can choose between TinyMCE  or FckEditor.

Turning on wysiwyg editing requires two steps:

 

  • download one of the editors mentioned above and extract it in /webroot/js folder (you have to create structure like /webroot/js/tinymce/)
  • adding or changing configuration directive LilblogsPlugin.editor - like Configure::write('LilblogsPlugin.editor', 'tinymce');

I am planning to further improvements in this field - like creating editor themes, adding styles and adding file/image/media managers.

LilBayes - bayesian filtering for CakePHP

Posted on 21/8/08 (3 comments)

Currently I am working on bayesian filtering implementation as CakePHP behavior. I am already using bayesian filtering for comments in LilBlogs system. Currently it categorizes comments in two obvious groups: spam and ham. It just works!

This bayesian filtering is awsome. It works practicly without mistakes on one of my sites with heavier traffic and substential comment count using LilBlogs. The only downside of Bayes filtering is that it needs some learning before it becomes efficient. After this initial faze it gets only better and better.

Stay tuned! I will be releasing LilBayes behaviour shortly.