Ramblings

Determine state from postcode in Australia

I had a little dilemma where I was importing a list of stores, but a required field was state. I had the postcodes for all these stores so I figured I could determine the state. I’ve dealt with postcode data before but I really couldn’t be bothered downloading that database from Australia Post (it’s pretty big). I’ve always known that postcodes starting with 2 are in NSW, 3 are in VIC, 4 are in QLD etc. so I set out to find what exactly determines the state from postcode.

The result was this article http://en.wikipedia.org/wiki/Postcodes_in_Australia#States_and_territories

And the PHP function is:

	/**
	/**
	* Returns the state for a postcode.
	* eg. NSW
	* 
	* @link http://en.wikipedia.org/wiki/Postcodes_in_Australia#States_and_territories
	*/
	function findState($postcode) {
		$ranges = array(
			'NSW' => array(
				1000, 1999,
				2000, 2599,
				2619, 2898,
				2921, 2999
			),
			'ACT' => array(
				200, 299,
				2600, 2618,
				2900, 2920
			),
			'VIC' => array(
				3000, 3999,
				8000, 8999
			),
			'QLD' => array(
				4000, 4999,
				9000, 9999
			),
			'SA' => array(
				5000, 5999
			),
			'WA' => array(
				6000, 6797,
				6800, 6999
			),
			'TAS' => array(
				7000, 7999
			),
			'NT' => array(
				800, 999
			)
		);
		$exceptions = array(
			872 => 'NT',
			2540 => 'NSW',
			2611 => 'ACT',
			2620 => 'NSW',
			3500 => 'VIC',
			3585 => 'VIC',
			3586 => 'VIC',
			3644 => 'VIC',
			3707 => 'VIC',
			2899 => 'NSW',
			6798 => 'WA',
			6799 => 'WA',
			7151 => 'TAS'
		);
 
		$postcode = intval($postcode);
		if ( array_key_exists($postcode, $exceptions) ) {
			return $exceptions[$postcode];
		}
 
		foreach ($ranges as $state => $range)
		{
			$c = count($range);
			for ($i = 0; $i < $c; $i+=2) {
				$min = $range[$i];
				$max = $range[$i+1];
				if ( $postcode >= $min && $postcode <= $max ) {
					return $state;
				}
			}
		}
 
		return null;
	}

View Harvest invoices with your iPhone

Invoices are at the core of every business, it’s what drives the money into any freelance / business gig. Harvest is awesome for tracking time, invoices and estimates. Unfortunately their iPhone support is limited to time tracking, but really I care about money and so I needed an iPhone interface for invoices ASAP.

So last night I mashed up a quick web app which used jqTouch and the Harvest API. Now I can check my invoices from anywhere, and I abstracted it enough so anybody out there can use it.

Check it out (on your iPhone) at http://scripts.waww.com.au/harvest_invoices

No domain? No problems! Fallback translations in CakePHP

A common methodology in CakePHP is that if something doesn’t exist somewhere, it will look in the next logical spot until it finds it. This is evident with themed views, controllers, models, basically everything in the framework. That’s why I was a little bit suprised dismorning when I was looking into translations within a domain. If the phrase didn’t exist in the domain then it would simply return the original string, without looking inside the default domain.

So I came up with a function that would first look inside the custom domain and if nothing was found then it would naturally work inside the default domain. The function is named __dd(), which is short for default domain.

/**
 * Allows you to override the current domain for a single message lookup.
 * If the msg doesn't exist in the current domain, it will use the default
 * domain.
 *
 * @param string $domain Domain
 * @param string $msg String to translate
 * @param string $return true to return, false to echo
 * @return translated string if $return is false string will be echoed
 */
function __dd($domain, $msg, $return = false) {
	$d = __d($domain, $msg, true);
	if ( $d == $msg ) {
		return __($msg, $return);
	}
	if ( $return ) {
		return $d;
	}
	else {
		echo $d;
	}
}

I’m not sure why the CakePHP gods haven’t done it this way, I’m pretty sure there would be a reason though. The idea came to me with the recent feature addition of domains to my settings plugin (documentation).

CakePHP custom error class doesn’t work when providing translations

So today I was trying to make a form that was friendly between CakePHP and UniForm. Things were going really well, I created a standard options array which converted the markup into UniForm friendly markup, it all looked good until I went to submit. Cake was somehow forgetting the error class I specified. Let’s get to some examples…

The Model Validation Rules

<?php
class Product extends AppModel {
 
	var $name = 'Product';
 
	var $validate = array(
	    'title' => array(
    		'notEmpty' => array(
    			'rule' => 'notEmpty'
    		),
	    )
	);
}
?>

The View Template

<div class="products form">
<?php echo $form->create('Product', array('type' => 'file'));?>
	<fieldset>
 		<legend><?php __('Add Product');?></legend>
	<?php
		$options = array(
			'error' => array(
				'class' => 'errorField',
				'email' => 'Please enter a product title'
			)
		);
		echo $form->input('title', $options);
		echo $this->element('attachments', array('plugin' => 'media'))
	?>
	</fieldset>
<?php echo $form->end('Submit');?>
</div>
<div class="actions">
	<ul>
		<li><?php echo $html->link(__('List Products', true), array('action' => 'index'));?></li>
	</ul>
</div>

Which, when submitting the form with an empty product title would you give the error message of “Please enter a product title” inside a div with the class “errorField”, however Cake forgets the custom error class and gives the “error-message” class.

What Happened?

I got this code…

<div class="input text error">
    <label for="ProductTitle">Title</label>
    <input type="text" class="form-error" id="ProductTitle" value="" maxlength="255" name="data[Product][title]"/>
    <div class="error-message">Please enter a product title</div>
</div>

What was expected?

This…

<div class="input text error">
    <label for="ProductTitle">Title</label>
    <input type="text" class="form-error" id="ProductTitle" value="" maxlength="255" name="data[Product][title]"/>
    <div class="errorField">Please enter a product title</div>
</div>

The Fix

After looking through the core code, I traced the problem down into the error function of the FormHelper. The input() function only passes the $field name and the $text variable as the options submitted in the original “error” key to the options passed to the input() function.

In the error function, if the $text array contains the key of the error type, then it disregards the rest of the $text array, forgetting the custom error class. What we need to do is set the $options variable to $text if it is in array, as well as pull the custom message for the error type, kapeech? OK, I’ll just give you the code…

Before

411
412
413
414
415
416
if (is_array($text) && isset($text[$error])) {
	$text = $text[$error];
} elseif (is_array($text)) {
	$options = array_merge($options, $text);
	$text = null;
}

After

411
412
413
414
if (is_array($text)) {
	$options = array_merge($options, $text);
	$text = isset($text[$error]) ? $text[$error] : null;
}

I never like touching core code so I’m hoking the cake devs will be able to get on this for the next release, and it looks like I just missed the next release, as CakePHP 1.2.5 just popped up on my reader.

I have submitted a ticket at http://code.cakephp.org/tickets/view/76

Ever wondered where the **** that query came from in CakePHP?

I’ve always been frustrated looking at the masses of queries generated by my CakePHP applications and really wondering where they originated from. It would be extremely useful when performing debugging. Sure enough I started looking into cakephp datasources and found that I could implement a solution without even touching the core!

Something like…

sqllog

I’ve released the datasource to make this happen as part of my Util plugin for CakePHP. To get started please see the Wiki page.

Util is just a plugin full of unrelated libraries for CakePHP, most of the code there is from other people.

Currently only for MySQL but you only have to change one line for different databases.

CakePHP Layouts – Leave your default as default

For the past few months I’ve been following a convention of leaving the default.ctp as the default.ctp and putting my main layout in app.ctp, the only reason being is that when you get those missing controller, missing view, missing method errors it looks ugly in any custom default layout.

You can get your application to use the app.ctp layout by specifying

var $layout = 'app';

in your AppController.php. The only problem with this is that cake will still use this layout when an error occurs, to overcome this we create the app_error.php file and override _outputMessage() method.

<?php
class AppError extends ErrorHandler
{
/**
 * Output message
 *
 * @access protected
 */
	function _outputMessage($template) {
		$this->controller->layout = 'default';
		return parent::_outputMessage($template);
	}
}
?>

The above code reverts the layout back to the default.ctp layout whenever an error occurs so you can see those nasty but nice CakePHP errors.

Tip: Debug on a live site only for you (yes even with a shared IP)

There was a bug on one of our live sites which required some level of debugging. Unfortunately with my CakePHP app turning on debugging meant a whole lot of nastiness that would appear for users of the website. I needed to see the debug information on the live site without affecting any other users (including my bosses in the same office, which meant I couldn’t just turn on debugging for our office IP address).

Here’s what I did:

  • Download User Agent Switcher
  • Setup a new User Agent and copy “Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 FirePHP/0.3  Debugger” in there. It’s important to keep the firefox related information in there as this allows hacks and stuff to continue knowing that it’s Firefox making the request
  • Setup a condition in your code that checks if “Debugger” is present in the user agent string and turns on debugging if so.

My condition code looks this:

if ( strpos($_SERVER['HTTP_USER_AGENT'], 'Debugger') !== false ) {
	$debug = 2;
}

Enjoy full debug freedom on a production site!

Flash Mailto Link Not Working

I was dumb founded dismorning when the “send to friend” link in my flash file wasn’t working, in any browser. If I loaded that SWF file directly in the browser, it worked perfectly.

I did some investigating around the Internet and couldn’t find any reason why it shouldn’t be working. I began thinking what was different about this SWF file which would cause it not to work.

Then it hit me…

You need to assign the allowScriptAccess parameter to “always” if the SWF file is loaded from another domain for mailto links to work.

Upgrading SVN to 1.5 on Leopard

I use the awesome app Versions for my SVN needs on my Macbook Pro. Unfortunately, it automaticaly updated my working copies to version 1.5, which wasn’t compatible with the native command line SVN pre-installed with leopard.

Sometimes, I need to go back to the command line to add individual folders (rather than the folder and everything inside it). I can’t imagine this would be hard for the Versions guys to implement, come on!

However they have impressed me with offering a Mac SVN 1.5 DMG download to install the latest subversion with ease! It’s downloading at the moment and I’ll just assume it’s going to work.

You rock, Versions App.

ExternalInterface.addCallback – Object doesn’t support property or method

If your facing some EXTREMELY frustating JavaScript mesages in IE when trying to a utilise a call method from javascript into action script, maybe I can help. I’ve just spent 4 hours tearing my hair out about this problem and finally got it solved. It was so frustrating and all google searches were returning lots of unrelated information with alot of fixes but no ultimate solution (for my problem anyway).

So, to make sure that IE can use ExternalInterface:

  • To get a reference to the flash movie, it shouldn’t be inside a form. If it must, in the case of .NET or Qcodo, use the solutions found at http://www.adobe.com/go/kb400730 OR use the swfobject.getObjectById function to get a reference.
  • Update to the latest version of SWFObject (at time of writing this is 2.1) to place the flash in your page.
  • The flash file must be visible on the page

I’ve placed the last point in bold as nowhere on the web was this a documented fix. I assume Internet Explorer might think this is “security” feature. I was displaying a modal window on top of the flash, which hid the flash from view as the flash can appear ontop of the modal window if the wmode parameter isn’t set to transparent.

The modal window needed to interact with the flash file, but couldn’t in IE because it was hidden.

When CakePHP save doesn’t work – a possible solution

After some frustrating experiences with Cake’s save() and saveAll() functions. They would simply not save the data! I thought the whole thing was messed up and I actually wrote a massive blog post about how to fix it last night. However in my editing and checking dismorning I found the following snippet of code in model.php’s method set.

731
732
733
734
735
736
737
738
739
740
741
if (is_array($one)) {
	$data = $one;
	if (empty($one[$this->alias])) {
		$keys = array_keys($one);
		if (in_array($keys[0], array_keys($this->_schema))) {
			$data = array($this->alias => $one);
		}
	}
} else {
	$data = array($this->alias => array($one => $two));
}

I was trying to save some data which had some fields that wasn’t going to be saved into the database, these were things like credit card numbers which I would process in beforeSave etc. Since the code above will disable your save attempts if the first key in your data array is not a field in the model schema.

Where is Weave 0.2????

For those of you not in the recent know! Google has ceased it’s development of the EXTREMELY useful Google Browser Sync for Firefox. Which is sadly incompatible with the latest version of Firefox, Firefox 3. I could no longer have a seamless browsing experience between home and work, my life was in shatters.

In true Firefox styles…

Our Firefox at work, hallowed be your name, your bookmarks and passwords, your settings be saved at work as it is in home.

(If you don’t know what I’m talking about type “about:mozilla” in your Firefox 3 address bar).

My world was saved a few days later when I discovered the Mozilla made Weave. I tried Weave before and wasn’t too impressed with the way it worked. It was a 0.1 release after all. Maybe I was biased because Google Browser Sync was soo good. However, now I was forced to install Weave again and it has seemed to be far more stable than my last attempt. I especially like the idea how there can be a web interface for all your bookmarks.

I use a PC at work and come home to my shiny Mac Book Pro at night, which I hear is common place nowadays. Weave on my Mac seems to hang itself every 5 minutes and the smooth rotating icon in my Firefox status bar slows down to about 5 pixels every second.

So, as the title states. Where is Weave 0.2? The latest update from Mozilla suggests…

Update: We expect this major update to Weave to be available for wider testing and feedback by Friday, June 20th.

At time of writing is June 26th. I’m wondering, as are others where is Weave 0.2??

In the final moments of this article, I discovered this article from Mozilla. I hope to see it tomorrow, or if not Monday. That gives the Mozilla guys a weekend!

Combine your JS & CSS files for faster loading..

With the relatively recent publication of YSlow, it brought to my attention that while seperating your CSS and JS files into seperate files for a more structured application – it can greatly reduce the load time for your users. This solution aims to solve that by keeping your files seperate, and implementing the advantages of cake so ultimately your users will only have to download one aggregated JS and CSS file.

Read More »

Qcodo vs CakePHP

At work our main PHP framework is Qcodo. However I’m in love with CakePHP. So I set out on creating a presentation which is a comparision of the two frameworks. In the end I concluded that it was like comparing apples and oranges. Qcodo seems like a port of ASP.NET, relies havily on Code Generation whereas CakePHP seems like a port of Ruby on Rails and relies on Metaprogramming.

http://docs.google.com/present/view?id=dph9vvc_89fjgp24cq