Published: on 23/6/08 | Comments (11)
Ever needed a reliable currency converter, that you can just drop into your application and never worry about manually updating the rates for.
Well I do for a current application I have been developing and I decided that it would make an ideal example for demonstrating how easy it is to create usefull CakePHP components.
So after a little Googling, I found that the Central European Bank, rather conveniently publishes an XML feed of rates against the Euro that is updated nightly, by using the HttpSocket class, the XML class and a little bit of code, I created the following component which you are welcome to copy and use in any way you please.
To use the component, simply copy the code into a file called currency.php and place that in your app/controllers/components folder, add 'Currency' to the $components array of the controller you need the functionality within (or app_controller if you want it avaliable site wide) then use:
$result = $this->Currency->convert($amount,$fromCurrency,$toCurrency,$decimalPlaces);
Where $amount is the amount of money you want to convert, $fromCurrency is a 3 Character currency code from the table shown at the bottom of this article that you are converting from, $toCurrency is the 3 Character currency code that you want to convert to, and decimalPlaces is the number of decimal places you want the result returned to, so for example:
$result = $this->Currency->convert(2.5, 'EUR', 'USD');
will give the US Dollar value for €2.50.
I have added a second function called table which returns an array of rates where each rate is expressed against a given base rate, for example:
$result = $this->currency->table('USD',4);
Will return an array of currencies compared to US Dollars and to 4 decimal places.
O.K. so explaination over, here is the code and a table of currency codes, there are 35 different currencies all told so should be enough to cover most needs.
<?php
class CurrencyComponent extends object{
/**
* $components
*
* Array of components required
*
* @var $components array
* @access public
*/
var $components = array('Session');
/**
* convert
*
* Converts the $amount from $fromCurrency to $toCurrency, formatted to
* $decimals decimal places
*
* @return float [Converted Currency Amount]
* @param $amount float
* @param $fromCurrency string
* @param $toCurrency string
* @param $decimals integer[optional]default=2
*/
function convert($amount,$fromCurrency,$toCurrency,$decimals = 2){
//Get the rate table
$rates = $this->__retrieveCurrencies();
//Return result of conversion
return number_format($amount/$rates[$fromCurrency]*$rates[$toCurrency],$decimals);
}
/**
* table
*
* Returns an array of rates in comparison the the $base currency given to $decimals
* number of decimal places
*
* @return array
* @param $base string[optional]default='EUR'
* @param $decimals integer[optional]default=2
*/
function table($base='EUR',$decimals=2){
//Create array to holds rates
$rateTable = array();
//Get rate table array
$rates = $this->__retrieveCurrencies();
//Iterate throught each rate converting it against $base
foreach($rates as $key=>$value){
$rateTable[$key]=number_format(1/$rates[$base]*$rates[$key],$decimals);
}
//Return result array
return $rateTable;
}
function __retrieveCurrencies(){
//Check whther we have already cached the currencies this session...
if(!$this->Session->check('Currencies')){
//...we haven't, so load utility classes needed
App::import('HttpSocket');
App::import('Xml');
//Create an http socket
$http =& new HttpSocket();
//And retrieve rates as an XML object
$currencies =& new XML($http->get('http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'));
//Convert XML to array
$currencies = Set::reverse($currencies);
//Filter down to just the rates
$currencies = $currencies['Envelope']['Cube']['Cube']['Cube'];
//Create an array to hold the rates
$currencyList = array();
//European Central bank gives us everything against Euro so add this manually
$currencyList['EUR']=1;
//Now iterate through and add the rates provided
foreach($currencies as $currency){
$currencyList[$currency['currency']]=$currency['rate'];
}
//Save to session
$this->Session->write('Currencies',$currencyList);
}
//Return rates array from session
return $this->Session->read('Currencies');
}
}
?>
| Currency | Code |
|---|---|
| Euro | EUR |
| US Dollar | USD |
| Japanese Yen | JPY |
| Bulgarian Lev | BGN |
| Czech Koruna | CZK |
| Danish Krone | DKK |
| Estonian Kroon | EEK |
| Pound Sterling | GBP |
| Hungarian Forint | HUF |
| Lithuanian Litas | LTL |
| Latvian Lats | LVL |
| Polish Zloty | PLN |
| New Romanian Leu | RON |
| Swedish Krona | SEK |
| Slovak Koruna | SKK |
| Swiss Franc | CHF |
| Icelandic Krona | ISK |
| Norwegian Krone | NOK |
| Croatian Kuna | HRK |
| Russian Rouble | RUB |
| New Turkish Lira | TRY |
| Australian Dollar | AUD |
| Brasilian Real | BRL |
| Canadian Dollar | CAD |
| Chinese Yuan Renminbi | CNY |
| Hong Kong Dollar | HKD |
| Indonesian Rupiah | IDR |
| South Korean Won | KRW |
| Mexican Peso | MXN |
| Malaysian Ringgit | MYR |
| New Zealand Dollar | NZD |
| Philippine Peso | PHP |
| Singapore Dollar | SGD |
| Thai Baht | THB |
| South African Rand | ZAR |
Hope you find this component usefull, let me know if you come up with any additional functionality you'd like it to include and 'till next time
hey, nice idea, but i would prefer to cache the currency-xml-file not in a session but on the filesystem or database. your component have to ask the ecb once a day, not every new session-user ;)
bye, butters
@butters: Valid point and if anyone would like me to alter the component to do this then let me know through these comments and I will make the changes to illustrate how this is done simply with cakephp.
Also if anyone has any requests or suggestions for other components or code snippets either drop me a line(see my contact page for email address) or leave a comment here.
Best Regards
Peter
@Peter: Implementing a cache makes more sense for app speed, bandwidth, and to not getting everyone's access to the ECB rates blocked. Anything you could add about how to cache a non-view would be helpful. Off the top of my head I think it just requires adding the Cache helper and "var cacheAction = '+1 hour';" in your component and then calling "$this->cacheAction = $currencyList;" instead of "$this->Session->write('Currencies',$currencyList);". Will that work from within a component & not a controller?
@butters: Good idea about adding caching. I think caching to the filesystem makes more sense than to the DB, unless you want to write an extra model to handle that data. Any reason the DB would be better?
Im a beginner at php I wan to apply this component to my website...ive supplied you with a demo link below
can this component do the job like the site below
http://www.go2africa.com/south-africa/cape-town/accommodation
Here is an array of the currency codes that can be fed into a CakePHP select box.
array('EUR'=>'Euro', 'USD'=>'US Dollar', 'JPY'=>'Japanese Yen', 'BGN'=>'Bulgarian Lev', 'CZK'=>'Czech Koruna', 'DKK'=>'Danish Krone', 'EEK'=>'Estonian Kroon', 'GBP'=>'Pound Sterling', 'HUF'=>'Hungarian Forint', 'LTL'=>'Lithuanian Litas', 'LVL'=>'Latvian Lats', 'PLN'=>'Polish Zloty', 'RON'=>'New Romanian Leu', 'SEK'=>'Swedish Krona', 'SKK'=>'Slovak Koruna', 'CHF'=>'Swiss Franc', 'ISK'=>'Icelandic Krona', 'NOK'=>'Norwegian Krone', 'HRK'=>'Croatian Kuna', 'RUB'=>'Russian Rouble', 'TRY'=>'New Turkish Lira', 'AUD'=>'Australian Dollar', 'BRL'=>'Brasilian Real', 'CAD'=>'Canadian Dollar', 'CNY'=>'Chinese Yuan Renminbi', 'HKD'=>'Hong Kong Dollar', 'IDR'=>'Indonesian Rupiah', 'KRW'=>'South Korean Won', 'MXN'=>'Mexican Peso', 'MYR'=>'Malaysian Ringgit', 'NZD'=>'New Zealand Dollar', 'PHP'=>'Philippine Peso', 'SGD'=>'Singapore Dollar', 'THB'=>'Thai Baht', 'ZAR'=>'South African Rand' );
I rewrote the __retrieveCurrencies function to cache the results to a file for 24 hours. See below:
function __retrieveCurrencies()
{
//Check whther we have already cached the currencies this session...
Cache::config(null, array('engine'=>'File', 'path'=>CACHE));
$currencyList = stripslashes_deep(Cache::read('currencies'));
if(empty($currencyList))
{
//...we haven't, so load utility classes needed
App::import('HttpSocket');
App::import('Xml');
//Create an http socket
$http =& new HttpSocket();
//And retrieve rates as an XML object
$currencies =& new XML($http->get('http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'));
//Convert XML to array
$currencies = Set::reverse($currencies);
//Filter down to just the rates
$currencies = $currencies['Envelope']['Cube']['Cube']['Cube'];
//Create an array to hold the rates
$currencyList = array();
//European Central bank gives us everything against Euro so add this manually
$currencyList['EUR']=1;
//Now iterate through and add the rates provided
foreach($currencies as $currency){
$currencyList[$currency['currency']]=$currency['rate'];
}
$cache = Cache::write('currencies', $currencyList, array('duration'=>86400, 'config'=>null));
}
//Return rates array from session
return $currencyList;
}
Studio Canaria is the web site of freelance web developer, Peter Butler. Articles on this site relate to designing, developing and marketing modern web applications.
CakePHP Auth Component - Users, Groups & Permissions Revisited
CakePHP Auth Component - Users, Groups & Permissions