Version: current

      1 <?php
      2 /**
      3  * Partially based on Zend_Currency.
      4  *
      5  * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
      6  * @license   http://framework.zend.com/license/new-bsd  New BSD License
      7  * @version   $Id: Currency.php 6137 2007-08-19 14:55:27Z shreef $
      8  */
      9
     10 require_once 'Zend/Currency.php';
     11
     12 /**
     13  * Implements the "Money" pattern.
     14  *
     15  * @see http://www.martinfowler.com/eaaCatalog/money.html
     16  *
     17  * @todo Support different ways of rounding
     18  * @todo Equality operators
     19  * @todo Addition, substraction and allocation of values
     20  * @todo Model validation for $allowedCurrencies
     21  *
     22  * @package sapphire
     23  * @subpackage model
     24  */
     25 class Money extends DBField implements CompositeDBField {
     26
     27     /**
     28      * @var string $getCurrency()
     29      */
     30     protected $currency;
     31
     32     /**
     33      * @var float $currencyAmount
     34      */
     35     protected $amount;
     36
     37     /**
     38      * @var boolean $isChanged
     39      */
     40     protected $isChanged = false;
     41
     42     /**
     43      * @var string $locale
     44      */
     45     protected $locale = null;
     46
     47     /**
     48      * @var Zend_Currency
     49      */
     50     protected $currencyLib;
     51
     52     /**
     53      * Limit the currencies
     54      * @var array $allowedCurrencies
     55      */
     56     protected $allowedCurrencies;
     57
     58     /**
     59      * @param array
     60      */
     61     static $composite_db = array(
     62         "Currency" => "Varchar(3)",
     63         "Amount" => 'Decimal(19,4)'
     64     );
     65
     66     function __construct($name = null) {
     67         $this->currencyLib = new Zend_Currency(null, i18n::default_locale());
     68
     69         parent::__construct($name);
     70     }
     71
     72     function compositeDatabaseFields() {
     73         return self::$composite_db;
     74     }
     75
     76     function requireField() {
     77         $fields = $this->compositeDatabaseFields();
     78         if($fields) foreach($fields as $name => $type){
     79             DB::requireField($this->tableName, $this->name.$name, $type);
     80         }
     81     }
     82
     83     function writeToManipulation(&$manipulation) {
     84         if($this->getCurrency()) {
     85             $manipulation['fields'][$this->name.'Currency'] = $this->prepValueForDB($this->getCurrency());
     86         } else {
     87             $manipulation['fields'][$this->name.'Currency'] = DBField::create('Varchar', $this->getCurrency())->nullValue();
     88         }
     89
     90         if($this->getAmount()) {
     91             $manipulation['fields'][$this->name.'Amount'] = $this->getAmount();
     92         } else {
     93             $manipulation['fields'][$this->name.'Amount'] = DBField::create('Decimal', $this->getAmount())->nullValue();
     94         }
     95     }
     96
     97     function addToQuery(&$query) {
     98         parent::addToQuery($query);
     99         $query->select[] = sprintf('"%sAmount"', $this->name);
    100         $query->select[] = sprintf('"%sCurrency"', $this->name);
    101     }
    102
    103     function setValue($value, $record = null, $markChanged = true) {
    104         // @todo Allow resetting value to NULL through Money $value field
    105         if ($value instanceof Money && $value->hasValue()) {
    106             $this->setCurrency($value->getCurrency(), $markChanged);
    107             $this->setAmount($value->getAmount(), $markChanged);
    108             if($markChanged) $this->isChanged = true;
    109         } else if($record && isset($record[$this->name . 'Currency']) && isset($record[$this->name . 'Amount'])) {
    110             if($record[$this->name . 'Currency'] && $record[$this->name . 'Amount']) {
    111                 $this->setCurrency($record[$this->name . 'Currency'], $markChanged);
    112                 $this->setAmount($record[$this->name . 'Amount'], $markChanged);
    113             } else {
    114                 $this->value = $this->nullValue();
    115             }
    116             if($markChanged) $this->isChanged = true;
    117         } else if (is_array($value)) {
    118             if (array_key_exists('Currency', $value)) {
    119                 $this->setCurrency($value['Currency'], $markChanged);
    120             }
    121             if (array_key_exists('Amount', $value)) {
    122                 $this->setAmount($value['Amount'], $markChanged);
    123             }
    124             if($markChanged) $this->isChanged = true;
    125         } else {
    126             // @todo Allow to reset a money value by passing in NULL
    127             //user_error('Invalid value in Money->setValue()', E_USER_ERROR);
    128         }
    129     }
    130
    131     /**
    132      * @return string
    133      */
    134     function Nice($options = array()) {
    135         $amount = $this->getAmount();
    136         if(!isset($options['display'])) $options['display'] = Zend_Currency::USE_SYMBOL;
    137         if(!isset($options['currency'])) $options['currency'] = $this->getCurrency();
    138         if(!isset($options['symbol'])) $options['symbol'] = $this->currencyLib->getSymbol($this->getCurrency(), $this->getLocale());
    139         return (is_numeric($amount)) ? $this->currencyLib->toCurrency($amount, $options) : '';
    140     }
    141
    142     /**
    143      * @return string
    144      */
    145     function NiceWithShortname($options = array()){
    146         $options['display'] = Zend_Currency::USE_SHORTNAME;
    147         return $this->Nice($options);
    148     }
    149
    150     /**
    151      * @return string
    152      */
    153     function NiceWithName($options = array()){
    154         $options['display'] = Zend_Currency::USE_NAME;
    155         return $this->Nice($options);
    156     }
    157
    158     /**
    159      * @return string
    160      */
    161     function getCurrency() {
    162         return $this->currency;
    163     }
    164
    165     /**
    166      * @param string
    167      */
    168     function setCurrency($currency, $markChanged = true) {
    169         $this->currency = $currency;
    170         if($markChanged) $this->isChanged = true;
    171     }
    172
    173     /**
    174      * @todo Return casted Float DBField?
    175      *
    176      * @return float
    177      */
    178     function getAmount() {
    179         return $this->amount;
    180     }
    181
    182     /**
    183      * @param float $amount
    184      */
    185     function setAmount($amount, $markChanged = true) {
    186         $this->amount = (float)$amount;
    187         if($markChanged) $this->isChanged = true;
    188     }
    189
    190     /**
    191      * @return boolean
    192      */
    193     function hasValue() {
    194         return ($this->getCurrency() && is_numeric($this->getAmount()));
    195     }
    196
    197     /**
    198      * @return boolean
    199      */
    200     function hasAmount() {
    201         return (int)$this->getAmount() != '0';
    202     }
    203
    204     function isChanged() {
    205         return $this->isChanged;
    206     }
    207
    208     /**
    209      * @param string $locale
    210      */
    211     function setLocale($locale) {
    212         $this->locale = $locale;
    213         $this->currencyLib->setLocale($locale);
    214     }
    215
    216     /**
    217      * @return string
    218      */
    219     function getLocale() {
    220         return ($this->locale) ? $this->locale : i18n::get_locale();
    221     }
    222
    223     /**
    224      * @return string
    225      */
    226     function getSymbol($currency = null, $locale = null) {
    227
    228         if($locale === null) $locale = $this->getLocale();
    229         if($currency === null) $currency = $this->getCurrency();
    230
    231         return $this->currencyLib->getSymbol($currency, $locale);
    232     }
    233
    234     /**
    235      * @return string
    236      */
    237     function getShortName($currency = null, $locale = null) {
    238         if($locale === null) $locale = $this->getLocale();
    239         if($currency === null) $currency = $this->getCurrency();
    240
    241         return $this->currencyLib->getShortName($currency, $locale);
    242     }
    243
    244     /**
    245      * @return string
    246      */
    247     function getName($currency = null, $locale = null) {
    248         if($locale === null) $locale = $this->getLocale();
    249         if($currency === null) $currency = $this->getCurrency();
    250
    251         return $this->currencyLib->getName($currency, $locale);
    252     }
    253
    254     /**
    255      * @param array $arr
    256      */
    257     function setAllowedCurrencies($arr) {
    258         $this->allowedCurrencies = $arr;
    259     }
    260
    261     /**
    262      * @return array
    263      */
    264     function getAllowedCurrencies() {
    265         return $this->allowedCurrencies;
    266     }
    267
    268     /**
    269      * Returns a CompositeField instance used as a default
    270      * for form scaffolding.
    271      *
    272      * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()}
    273      *
    274      * @param string $title Optional. Localized title of the generated instance
    275      * @return FormField
    276      */
    277     public function scaffoldFormField($title = null) {
    278         $field = new MoneyField($this->name);
    279         $field->setAllowedCurrencies($this->getAllowedCurrencies());
    280         $field->setLocale($this->getLocale());
    281
    282         return $field;
    283     }
    284
    285     /**
    286      * For backwards compatibility reasons
    287      * (mainly with ecommerce module),
    288      * this returns the amount value of the field,
    289      * rather than a {@link Nice()} formatting.
    290      */
    291     function __toString() {
    292         return (string)$this->getAmount();
    293     }
    294 }
    295 ?>

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -