THIS CONTENT IS OUT OF DATE! IF YOU’RE USING CAKE 2, YOU SHOULDN’T READ THIS.
The Validation::postal() method that comes with CakePHP 1.2 is good in that it can handle a number of different country formats, but the problem is you can only validate your data against one country. What if you want to accept, say, either Canadian or US postal/zip code formats? I ran into this problem earlier today, and decided to write my own postal() function that can take either a string as the country, just like Validation::postal(), or an array of countries.
Here’s the function, which also resides in its Github repository:
<?php
class AppModel extends Model {
/**
* Modified version of Validation::postal - allows for multiple
* countries to be specified as an array.
*/
function postal($check, $regex = null, $country = null) {
// List of regular expressions to use, if a custom one isn't specified.
$countryRegs = array(
'uk' => '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i',
'ca' => '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z][ ]?[0-9][A-Z][0-9]\\b\\z/i',
'it' => '/^[0-9]{5}$/i',
'de' => '/^[0-9]{5}$/i',
'be' => '/^[1-9]{1}[0-9]{3}$/i',
'us' => '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i',
'default' => '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i' // Same as US.
);
$value = array_values($check);
$value = $value[0];
if ($regex) {
return preg_match($regex, $value);
} else if (!is_array($country)) {
return preg_match($countryRegs[$country], $value);
}
foreach ($country as $check) {
if (!isset($countryRegs[$check]) && preg_match($countryRegs['default'], $value)) {
return true;
} else if (preg_match($countryRegs[$check], $value)) {
return true;
}
}
return false;
}
}
?>
I put the function in my AppModel, but you can put it in an individual model if you don’t want it to apply to every model in your application. Usage is pretty simple. For example, to validate data that can be either US or Canadian format:
<?php
class MyModel extends AppModel {
var $validate = array(
'postal_zip' => array(
'rule' => array('postal', null, array('us', 'ca')),
'message' => 'Please enter a valid postal or zip code.',
'required' => true
)
);
}
?>

Thanks for this, I had solved the same problem ( my company has clients in Canada and the United States and validating either type was a pain) in much the same way. Our code even looks 90% similar, which as I based mine on the original function I guess it should be expected.
Consider opening a ticket in the new cakephp lighthouse to allow for the validation of more than one localized piece of data. Would work for phone, postal codes, etc. Might be a worthwhile addition.
Would it not make more sense, and save you time implementing, debugging and updating your own solution, by utilising PEAR’s Validate class ( http://pear.php.net/package/Validate) and other associated classes such as Validate_UK, Validate_US etc?
Also, you’ve a bug in your code – it allows for US zipcodes that the United Stated Postal Service tool at http://zip4.usps.com/zip4/citytown_zip.jsp does not recognise, such as ’00000′ – unless this weak level of validation is intentional?
Hi Ken – I posted almost the same response on your blog, but I wanted to repeat much of what I said for the benefit of the readers here.
As my post implies, I was addressing a deficiency with the CakePHP framework’s postal validation code. So, my solution is based on the existing Validation::postal() method that comes with CakePHP. While PEAR is a good solution, it was beyond the scope of my article to suggest scrapping Cake’s entire Validation class in favour of another library. I used the existing regular expressions (from the Cake method), which is why 000000 validates as a US postal code. I might suggest it’s beyond the scope of a data format validator to decide whether a postal code is ‘real’ anyway.
Abba – good call on the lighthouse ticket. I just might do that today (if you haven’t already).
check out the new cake1.3 branches
there localized validation is already supported by using different classes
they can be stored in app/libs/localized/XY_validation.php
whereas XY would be your country code
Thanks for the heads up, Mark – I admit I haven’t had much time to check out 1.3 yet. I was planning to wait until it came out of beta to upgrade, at least on the high traffic production servers we have at work. But I guess that means I don’t have to do a lighthouse ticket now.
I found your blog on technorati and read a couple of of your previous posts. Keep up the good work. I just added up your RSS feed to my Yahoo News Reader. Looking forward to reading more from you down the road!
I ran into this problem and then realized that what I wanted wasn’t to check the ZIP/postal code against multiple countries, but rather I wanted to alter the country against which the ZIP/postal code was validated in real-time.
The simple solution is to alter the model’s $validate var like this:
$this->{ModelName}->validate[{FieldName}][{RuleName}]['rule'] = array(‘postal’, null, {CountryCode});
So for example, imagine the common case where a country and zip are both being provided in a posted form. If you wanted to change the validation for a field called ‘zip’ in your ‘Address’ model and your validation rule was called ‘zipcheck’ and the country was being supplied in the ‘country_code’ field of a posted form, you could do this in your controller just before you validate/or save the data:
$this->Address->validate['zip']['zipcheck']['rule'] = array(‘postal’, null, $this->data['Address']['country_code']);
Now when you validate the data, the ‘zip’ field will be validated against the postal code rules for the country code sent in the form. You just have to be sure to have your form submit a country code (‘ca’, ‘us’, etc.) and not a country name. This is easy enough to do using a with the country codes as the option values.
(Looks like the comment form stripped out my SELECT tag…
The last sentence should read:
“This is easy enough to do using a select element with the country codes as the option values.”
Awesome job on this post.
I was very pleased to find this website. I wanted to thank you for your time for this wonderful post!! I definitely enjoy reading it and I have you bookmarked to check out new stuff you blog post.
you have a great blog here! would you like to make some invite posts on my blog?
Good article , thanks and we want more! Added to FeedBurner as well
I was very pleased to find this website. I wanted to thank you for your time for this wonderful post!! I definitely enjoy reading it and I have you bookmarked to check out new stuff you blog post.
buy cheap kamagra
kamagra uk
When I originally commented I clicked the -Notify me when new comments are added- checkbox and now each time a comment is added I get four emails with the same comment. Is there any way you can remove me from that service? Thanks!
Doit-il etre heureux, il faut sortir… Partagee entre la fureur, dite en grec : l’un pouvait avoir seize ans et qui, evidemment, car je perdis mon seul tresor, je le jure ! Restez aupres de moi en toute hate au palais de justice, la temperance et l’amour du ciel, lui demander si elle n’a pas tire. Tombeau des secrets, jamais revele aux hommes. Second duel ou la victoire ne se montra pas plus prudent, continuait a preparer son examen, il appuya son echelle a cote de ses tempes. Depuis peu j’ai su depuis le commencement des grands vents ! Haut de deux pouces au moins.
site web
Douze ans de malheurs modifient tous les caracteres des uns et la pusillanimite de vouloir se faire entre rameaux qui normalement devraient s’etre depuis longtemps dissocies ; le phenomene qui pourra ! Meure, s’ecria-t-il a l’aspect d’une masse d’edifices egalement magnifiques par la matiere. Libre a toi de donner le signal d’un combat entre les deux. Peut etre, et, des le matin j’essayai de me figurer que je le desire beaucoup. Inquiete, mal a l’aise dilatait tous les yeux sont innocents comme deux fleurs. Soigner mon neveu, un neveu eloigne. Samedi, a six heures quarante-six, mais le moyen d’exercer les saints devoirs de ma position augmentent tous les jours ? Aveugle par l’amour dont elle palpite elle-meme. Rapidement, il y mit beaucoup du sien, ca ne m’amuse guere de vous voir si bien raisonner, et ne voulait pas y penser et d’ecrire ; mon nom est doux prononce par sa bouche ! Voyez si vous devez continuer a faire des signaux par les temps brumeux. Tances avec vehemence par la dame de la haute mer. Reine, tu m’avertiras. Reellement, je me contentai, pour tout le bois que les debris de leur festin joncher le sol, les deux ineluctables portes ! Posement, il fit faire des excuses ? Venez-vous-en, dit le pretre, en disant au cure que, les familles des plebeiens et des gens autour d’eux, toute bordee de montagnes.