【问题标题】:Format money number_format PHP格式化货币 number_format PHP
【发布时间】:2021-12-25 03:16:12
【问题描述】:

我正在尝试从欧元格式化到拉丁美洲国家。但我无法让它们全部正确格式化。

这两行工作正常:

$currencies['ESP'] = array(2, ',', '.'); // Euro
$currencies['USD'] = array(2, '.', ','); // US Dollar

那些不起作用的是这些:

  1. 墨西哥 我有 1,800,520 美元的墨西哥比索,我想获得这个 结果 $ 3,698.00

    $currencies['MXN'] = array(3, ",", '.'); // México Peso
    
  2. 哥伦比亚 $ 2,097,106.36 哥伦比亚比索,我想得到 $ 104,637,255.96

    $currencies['COP'] = array(2, ',', '.'); // Colombiano Peso
    
  3. Argentina $53,609.02 阿根廷比索,我想得到 $10,490

    $currencies['ARS'] = array(2, ',', '.'); // Argentina Peso
    

有谁知道我做错了什么?感谢您的帮助。

我的函数示例:

/**
* @param self::$curr
* @return string
*/
public static function setCurrency($tipo) {
    // Creamos tipo moneda
    $tipoMoneda = ($tipo =='') ? self::$curr : $tipo;

    $moneda = match ($tipoMoneda) {
        'CLF' => "$",
        'COP' => "$",
        'ARS' => "$",
        'USD' => "$",
        'EUR' => "€",
        'MXN' => "$",
    };      
    return $moneda;
}   

/**
* Format price
* @param string
* @param string
*/
public static function toMoney($price,$tipo='') {
    $currencies['EUR'] = array(2, ',', '.'); // Euro
    $currencies['ESP'] = array(2, ',', '.'); // Euro
    $currencies['USD'] = array(2, '.', ','); // US Dollar
    $currencies['COP'] = array(2, ',', '.'); // Colombian Peso
    $currencies['MXN'] = array(3, ",", '.'); // Mexico Peso
    $currencies['CLP'] = array(0,  '', '.'); // Chilean Peso
    $currencies['ARS'] = array(2, ',', '.'); // Argentina Peso

    if ($tipo == '') :
        $money_format = number_format($price, ...$currencies[self::$curr]) . ' ' . self::setCurrency($tipo);
    else:
        $money_format = self::setCurrency($tipo) . number_format($price, ...$currencies[$tipo]);
    endif;
    return $money_format; 
} 

编辑: 我从数据库中得到的汇率

/**
* Calcular TAXES about original price base
* @param string
* @return string
*/
public static function CalcIva($valor, $arr =[]) {      
            
    // Get default IVA o by (USD-MXN) (COOKIE)
    $getIva = self::$defaultIva;
    
    // Price original
    $price = $valor;
    // Get taxes
    $iva = ($getIva / 100) * $price; 
    // Sum taxes to base price
    $precio = $price + $iva;    

    // On this line if $arr is not null i calculate 1.13 or some else x price
    if ($arr != null) : 
        // Calcul exchange rate (example: 1.13 * 20)
        $precio = $arr['cambio'] * $price;
    endif;
    // Price
    return $precio;     
 }

Example

设置cookie我在JS上做

/**
 * Select money (header)
 */
let moneda = document.getElementById('slc-moneda');
moneda.addEventListener('change', function (e) {
  // Get value option
  let tipo = this.value;
    // Not null
    if (tipo != 0) {
      // Value default, delete cookie
      if (tipo == 'EUR-ES') {
        // Eliminamos cookie, usamos configuracion por defecto
        delCookie('moneda');
        location.reload()
      // Set cookie - new money format
      } else {
        setCookie('moneda', tipo, 365)
        location.reload()     
      } 
    }          
    e.preventDefault()
  })

【问题讨论】:

  • 听起来您的格式没有问题 (number_format),但您想进行货币换算。货币转换需要一个汇率值,然后是一些基本的乘法运算。您粘贴的所有代码都没有处理这个问题。
  • @Raxi 我有另一个函数可以计算 1.13 x 20 € = x $,我选择了国家和相应的汇率,我更新了我的问题
  • 是的,添加的函数和 sqldata 可能与问题更相关,但我无法真正弄清楚问题出在哪里,因为这并不完整,而且它是西班牙语,让我很难跟着。 CalcIva 中有很多行我认为奇怪/可疑,但很难确定。总体设计方面,我会说您将很多东西混合在一起(在这些类方法中),它们真的不应该在同一个地方。
  • 既然您使用的是基于 OO 的设计,我想说产品(或订单)的货币价值应该与向用户呈现价值的方式完全分开(意思是他/她想要或选择的货币)。在同一个地方看到对 COOKIE 和转换数学 ($getIva / 100) * $price 的引用在这方面是一个尖叫的危险信号。
  • @raxi $getIVa 这是国家的税,在西班牙是21%。我在数据库中保存 decimal (5,2) 的价格,当我显示价格时,我在此函数 CalcIva() 中计算税金,例如 price + tax 。在我的 cookie 中,我输入了 USD-EC 的价值,所以我知道 USD-EC 来自厄瓜多尔。后来用explode() 分隔这个值。

标签: php money-format


【解决方案1】:

作为根据我之前的 cmets 添加的背景信息,以防您在分离所有相互关联的问题并将所有部分放在一起时遇到困难(我不知道您是否这样做);这是我过去用来解决类似问题的一些代码。我已根据您的数据模型/代码对其进行了调整并添加了一些 cmets:


就个人而言,由于将一半的货币信息保留在数据库中,另一半保留在代码中似乎很乱,我会在您的 monedas 数据库表中添加 4 列;即(以“厄瓜多尔”为例):

`currency_symbol`     => '$'
`decimal_separator`   => '.'
`thousands_separator` => ','
`decimals`            => 2

接下来,您要决定在 PHP 中为价格值使用什么数据类型。 我猜它们是您数据库中的DECIMALs,在这种情况下,您可以在 PHP 中使用字符串 ('65.99') 或浮点数 (65.99);通常string 是首选,因为它不会受到浮点数带来的所有奇怪的影响。

或者,您可以选择在数据库中以 美分 存储价格,这样您就可以在数据库和 PHP 中使用 INTEGERs (6599)。

假设您在数据库中使用DECIMAL,在PHP 中使用string;这样您就可以使用 PHP BCMath 函数可靠地执行计算。 我们还假设您数据库中的所有价格始终代表相同的货币(例如:您的企业的本地货币,假设它的EUR)。


由于价格在您的网店式应用程序中是一个复杂的值,因此您需要一个简单的值类来定义它们。

class Price {
    private $value;

    public function __construct($value) {
        $value = trim((string) $value);
        if (!is_numeric($value) || preg_match('#^(\-)?([0-9]+)(\.[0-9]{1,2})?$#D', $value) !== 1) throw Exception('Invalid price value');
        $this->value = $value;
    }

    public function getRawValue() {
        return $this->value;
    }

    // When printing a price (using echo for example), print it in its converted form (defined later)
    public function __toString() {
        return PriceLocalization::displayLocalPrice( $this );
    }
}

接下来,您需要一个对象来保存(或缓存)所有货币的所有信息:

class Currencies {
    protected static $data = null;

    protected static function pullData() {
        if (is_null(static::$data)) {
            $data = [];
            // Pull the currency/priceconversion info from the DB
            $rows = run_your_dbquery('SELECT * FROM `monera`');
            foreach ($rows as $row) {
                $row['id_moneda'] = (int) $row['id_moneda'];
                $row['decimals']  = (int) $row['decimals'];
                $data[( $row['id_moneda'] )] = $row;
            }
            // Cache the data incase we have to do more conversions on the current page
            static::$data = $data;
        }
        return static::$data;
    }

    // Returns the entire table of currency/priceconversion info from the DB
    public static function getAll() {
        return static::pullData();
    }

    // Returns one record out of the table of currency/priceconversion info (or exception if invalid)
    public static function getSpecific($id) {
        $data = static::pullData();
        if (array_key_exists($id, $data)) return $data[$id];
        throw new Exception('Bad input');
    }
}

另一个处理用户能够在会话范围内选择货币的对象

class UserCurrencySelection {

    // store the users choice in $_COOKIE or $_SESSION or the like (used by your currency-selection selectbox)
    public static function setUserPreference($choice) {
        $_SESSION['currencychoice'] = $choice;
        return true;
    }

    // read the raw value from $_COOKIE or $_SESSION or the like (if any)
    public static function getUserPreference() {
        return ($_SESSION['currencychoice'] ?? null);
    }

    // get either the active currency's record (if any), or otherwise the default record (throw exception if neither exists)
    public static function getActive() {
        try {
            if ($current = static::getUserPreference()) {
                return Currencies::getSpecific( $current );
            }
        } catch (Exception $e) {}
        return Currencies::getSpecific( 5 ); // <-- the id of the "default" currency (in this case 5 = EUR)
    }
}

最后,真正将所有东西联系在一起的类

class PriceLocalization {

    // display a specific price adjusted to the -active- currency (with the default currency as fallback)
    public static function displayLocalPrice(Price $price, array $style=[]) {
        $currencyinfo = UserCurrencySelection::getActive();
        return static::displayPriceAs($price, $currencyinfo, $style);
    }

    // display a specific price adjusted to a -specific- currency (eg: id=3 gives colombian price)
    public static function displayPriceInCurrency(Price $price, $id, array $style=[]) {
        $currencyinfo = Currencies::getSpecific( $id );
        return static::displayPriceAs($price, $currencyinfo, $style);
    }

    // perform the actual conversion and formatting
    protected static function displayPriceAs(Price $price, array $currencyinfo, array $style=[]) {
        /* $currencyinfo = [
          'id_monera'           => 4, 
          'moneda'              => 'USD',
          'pais'                => 'Ecuador',
          'ido'                 => 'EC',
          'cambio'              => '1.13',
          'impuesto'            => '12',
          'currency_symbol'     => '$',
          'decimal_separator'   => '.',
          'thousands_separator' => ',',
          'decimals'            => 2,
        ]; */
        // the original price:
        $value_src      = $price->getRawValue();                    
        // Multiply the original price with the conversion rate (`cambio`) to adjust it to this currency (giving us the pre-tax price)
        $value_excl     = bcmul($value_src, $currencyinfo['cambio']);   
        // Calculate the tax, by multiplying the adjusted price with the taxrate (`impuesto`*0.01 to adjust for it being a percentage)
        $tax            = bcmul($value_excl, bcmul('0.01', $currencyinfo['impuesto']));
        // Add the tax to the price to get the "price including tax"
        $value_incl     = bcadd($value_excl, $tax);
        // Decide which of the values you want to display (including or excluding tax)
        $value          = $value_incl;
        // Decide what we want to add before/after the numeric part of the price (the html-encoded version of the currency symbol)
        $label_prefix   = htmlentities( $currencyinfo['currency_symbol'] . ' ');
        $label_suffix   = ''; // or: htmlentities( ' ' . $currencyinfo['moneda']);
        // Change the number into human readable form
        $label          = number_format((float) $value, $currencyinfo['decimals'], $currencyinfo['decimal_separator'], $currencyinfo['thousands_separator']);
        // Convert that into html
        $label          = htmlentities($label);
        // Define some CSS classes to allow for styling
        $classes_prefix = 'p';
        $classes_number = 'v';
        $classes_suffix = 's';
        $classes_full   = 'price';
        // Now assemble all the pieces
        $html_prefix    = sprintf('<span class="%s">%s</span>',     htmlentities($classes_prefix),  $label_prefix);
        $html_number    = sprintf('<span class="%s">%s</span>',     htmlentities($classes_number),  $label);
        $html_suffix    = sprintf('<span class="%s">%s</span>',     htmlentities($classes_suffix),  $label_suffix);
        $html_full      = sprintf('<span class="%s">%s%s%s</span>', htmlentities($classes_full),    $html_prefix,  $html_number,  $html_suffix );
        // Done
        return $html_full;
    }
}

这就是它的要点。

您可以使用每个PriceLocalization 方法上可用的$style 参数将任意信息传递给displayPriceAs。根据该信息,您可以更改该函数组合其输出的方式。例如,您可以检查$style['include_tax'] 是否设置为true/false,并进行相应调整:

$value = (($style['include_tax'] ?? true) ? $value_incl : $value_excl);

您可以使用以下方式设置价格样式:

.price   { background-color: #EEE; }
.price.p { color: red;   font-weight: bold; }
.price.v { color: green; font-family: courier; }
.price.s { color: blue;  font-weight: bold; }

您还可以使用上面的$style 参数来引入其他类(在特定情况下)。


在您的应用程序中设置bcscale(15); 也是值得的,以确保以不会导致丢失部分便士的方式完成数学运算。


ps:在将代码改编为您的代码/数据模型后尚未对其进行测试,因此可能我在某处打错了字。

【讨论】:

  • 非常感谢您的回答,您的课程看起来很有趣。这周我会尝试看看我是否可以在我的代码中采用它,因为系统是 MVC,我的 +1
  • 啊,是的;我试图提出的主要观点主要是为了在那里保持一些关注点分离。显然我主要是在预测,因为我只看到了一个功能(所以我可能完全达到目标):)
  • 稍后我会在答案中完成我所有的功能,我也会做备份,看看我是否可以介绍他们的课程,再次感谢您的时间。
  • 我正在用你的班级做测试,我理解前 3 个并且我得到了结果,我修改了数据库,并添加了这些字段。但是我以后如何在 PriceLocalization 中打印价格。如果我这样做$price = 77.15; echo PriceLozalization::displayLocalPrice(Price $price, array $style=[]); 我会得到Parse error: syntax error, unexpected variable "$price", expecting ")"
  • 从数据库中读取价格时,如果价格在名为productprice 的列中,则执行 $row['productprice'] = new Price($row['productprice']);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-22
  • 2016-04-25
  • 2012-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多